feat: edit smart view predicate as json (#2012)
This commit is contained in:
@@ -23,11 +23,7 @@ const General: FunctionComponent<Props> = ({ viewControllerManager, application,
|
||||
<Persistence application={application} />
|
||||
<PlaintextDefaults application={application} />
|
||||
<Defaults application={application} />
|
||||
<SmartViews
|
||||
application={application}
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
/>
|
||||
<SmartViews application={application} featuresController={viewControllerManager.featuresController} />
|
||||
<Tools application={application} />
|
||||
<LabsPane application={application} />
|
||||
<Advanced
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import IconPicker from '@/Components/Icon/IconPicker'
|
||||
@@ -8,25 +7,35 @@ import ModalDialogButtons from '@/Components/Shared/ModalDialogButtons'
|
||||
import ModalDialogDescription from '@/Components/Shared/ModalDialogDescription'
|
||||
import ModalDialogLabel from '@/Components/Shared/ModalDialogLabel'
|
||||
import Spinner from '@/Components/Spinner/Spinner'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
import { SmartView, TagMutator } from '@standardnotes/snjs'
|
||||
import { Platform, SmartViewDefaultIconName } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { EditSmartViewModalController } from './EditSmartViewModalController'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
navigationController: NavigationController
|
||||
view: SmartView
|
||||
closeDialog: () => void
|
||||
controller: EditSmartViewModalController
|
||||
platform: Platform
|
||||
}
|
||||
|
||||
const EditSmartViewModal = ({ application, navigationController, view, closeDialog }: Props) => {
|
||||
const [title, setTitle] = useState(view.title)
|
||||
const EditSmartViewModal = ({ controller, platform }: Props) => {
|
||||
const {
|
||||
view,
|
||||
title,
|
||||
setTitle,
|
||||
predicateJson,
|
||||
setPredicateJson,
|
||||
isPredicateJsonValid,
|
||||
setIsPredicateJsonValid,
|
||||
icon,
|
||||
setIcon,
|
||||
save,
|
||||
isSaving,
|
||||
closeDialog,
|
||||
deleteView,
|
||||
} = controller
|
||||
|
||||
const titleInputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
const [selectedIcon, setSelectedIcon] = useState<string | undefined>(view.iconString)
|
||||
|
||||
const [isSaving, setIsSaving] = useState(false)
|
||||
const predicateJsonInputRef = useRef<HTMLTextAreaElement>(null)
|
||||
|
||||
const [shouldShowIconPicker, setShouldShowIconPicker] = useState(false)
|
||||
const iconPickerButtonRef = useRef<HTMLButtonElement>(null)
|
||||
@@ -41,29 +50,26 @@ const EditSmartViewModal = ({ application, navigationController, view, closeDial
|
||||
return
|
||||
}
|
||||
|
||||
setIsSaving(true)
|
||||
void save()
|
||||
}, [save, title.length])
|
||||
|
||||
await application.mutator.changeAndSaveItem<TagMutator>(view, (mutator) => {
|
||||
mutator.title = title
|
||||
mutator.iconString = selectedIcon || 'restore'
|
||||
})
|
||||
useEffect(() => {
|
||||
if (!predicateJsonInputRef.current) {
|
||||
return
|
||||
}
|
||||
|
||||
setIsSaving(false)
|
||||
closeDialog()
|
||||
}, [application.mutator, closeDialog, selectedIcon, title, view])
|
||||
if (isPredicateJsonValid === false) {
|
||||
predicateJsonInputRef.current.focus()
|
||||
}
|
||||
}, [isPredicateJsonValid])
|
||||
|
||||
const deleteSmartView = useCallback(async () => {
|
||||
void navigationController.remove(view, true)
|
||||
closeDialog()
|
||||
}, [closeDialog, navigationController, view])
|
||||
|
||||
const close = useCallback(() => {
|
||||
closeDialog()
|
||||
}, [closeDialog])
|
||||
if (!view) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalDialog>
|
||||
<ModalDialogLabel closeDialog={close}>Edit Smart View "{view.title}"</ModalDialogLabel>
|
||||
<ModalDialogLabel closeDialog={closeDialog}>Edit Smart View "{view.title}"</ModalDialogLabel>
|
||||
<ModalDialogDescription>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-2.5">
|
||||
@@ -85,7 +91,7 @@ const EditSmartViewModal = ({ application, navigationController, view, closeDial
|
||||
onClick={toggleIconPicker}
|
||||
ref={iconPickerButtonRef}
|
||||
>
|
||||
<Icon type={selectedIcon || 'restore'} />
|
||||
<Icon type={icon || SmartViewDefaultIconName} />
|
||||
</button>
|
||||
<Popover
|
||||
open={shouldShowIconPicker}
|
||||
@@ -96,28 +102,48 @@ const EditSmartViewModal = ({ application, navigationController, view, closeDial
|
||||
>
|
||||
<div className="p-2">
|
||||
<IconPicker
|
||||
selectedValue={selectedIcon || 'restore'}
|
||||
selectedValue={icon || SmartViewDefaultIconName}
|
||||
onIconChange={(value?: string | undefined) => {
|
||||
setSelectedIcon(value)
|
||||
setIcon(value || SmartViewDefaultIconName)
|
||||
toggleIconPicker()
|
||||
}}
|
||||
platform={application.platform}
|
||||
platform={platform}
|
||||
useIconGrid={true}
|
||||
portalDropdown={false}
|
||||
/>
|
||||
</div>
|
||||
</Popover>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2.5">
|
||||
<div className="text-sm font-semibold">Predicate:</div>
|
||||
<div className="flex flex-col overflow-hidden rounded-md border border-border">
|
||||
<textarea
|
||||
className="h-full min-h-[10rem] w-full flex-grow resize-none bg-default py-1.5 px-2.5 font-mono text-sm"
|
||||
value={predicateJson}
|
||||
onChange={(event) => {
|
||||
setPredicateJson(event.target.value)
|
||||
setIsPredicateJsonValid(true)
|
||||
}}
|
||||
spellCheck={false}
|
||||
ref={predicateJsonInputRef}
|
||||
/>
|
||||
{!isPredicateJsonValid && (
|
||||
<div className="border-t border-border px-2.5 py-1.5 text-sm text-danger">
|
||||
Invalid JSON. Double check your entry and try again.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ModalDialogDescription>
|
||||
<ModalDialogButtons>
|
||||
<Button className="mr-auto" disabled={isSaving} onClick={deleteSmartView} colorStyle="danger">
|
||||
<Button className="mr-auto" disabled={isSaving} onClick={deleteView} colorStyle="danger">
|
||||
Delete
|
||||
</Button>
|
||||
<Button disabled={isSaving} onClick={saveSmartView}>
|
||||
<Button disabled={isSaving} onClick={saveSmartView} primary colorStyle="info">
|
||||
{isSaving ? <Spinner className="h-4.5 w-4.5" /> : 'Save'}
|
||||
</Button>
|
||||
<Button disabled={isSaving} onClick={close}>
|
||||
<Button disabled={isSaving} onClick={closeDialog}>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalDialogButtons>
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { STRING_DELETE_TAG } from '@/Constants/Strings'
|
||||
import {
|
||||
predicateFromJson,
|
||||
PredicateJsonForm,
|
||||
SmartView,
|
||||
SmartViewDefaultIconName,
|
||||
SmartViewMutator,
|
||||
} from '@standardnotes/snjs'
|
||||
import { confirmDialog } from '@standardnotes/ui-services'
|
||||
import { action, makeObservable, observable } from 'mobx'
|
||||
|
||||
export class EditSmartViewModalController {
|
||||
title = ''
|
||||
icon: string = SmartViewDefaultIconName
|
||||
predicateJson = ''
|
||||
isPredicateJsonValid = false
|
||||
isSaving = false
|
||||
view: SmartView | undefined = undefined
|
||||
|
||||
constructor(private application: WebApplication) {
|
||||
makeObservable(this, {
|
||||
title: observable,
|
||||
icon: observable,
|
||||
predicateJson: observable,
|
||||
isPredicateJsonValid: observable,
|
||||
isSaving: observable,
|
||||
view: observable,
|
||||
|
||||
setTitle: action,
|
||||
setIcon: action,
|
||||
setPredicateJson: action,
|
||||
setIsPredicateJsonValid: action,
|
||||
setIsSaving: action,
|
||||
setView: action,
|
||||
})
|
||||
}
|
||||
|
||||
setTitle = (title: string) => {
|
||||
this.title = title
|
||||
}
|
||||
|
||||
setIcon = (icon: string) => {
|
||||
this.icon = icon
|
||||
}
|
||||
|
||||
setPredicateJson = (json: string) => {
|
||||
this.predicateJson = json
|
||||
}
|
||||
|
||||
setIsPredicateJsonValid = (isValid: boolean) => {
|
||||
this.isPredicateJsonValid = isValid
|
||||
}
|
||||
|
||||
setView = (view: SmartView | undefined) => {
|
||||
this.view = view
|
||||
|
||||
if (view) {
|
||||
this.setTitle(view.title)
|
||||
this.setIcon(view.iconString)
|
||||
this.setPredicateJson(JSON.stringify(view.predicate.toJson(), null, 2))
|
||||
this.setIsPredicateJsonValid(true)
|
||||
}
|
||||
}
|
||||
|
||||
setIsSaving = (isSaving: boolean) => {
|
||||
this.isSaving = isSaving
|
||||
}
|
||||
|
||||
closeDialog = () => {
|
||||
this.setView(undefined)
|
||||
this.setTitle('')
|
||||
this.setIcon(SmartViewDefaultIconName)
|
||||
this.setPredicateJson('')
|
||||
}
|
||||
|
||||
save = async () => {
|
||||
if (!this.view) {
|
||||
return
|
||||
}
|
||||
|
||||
this.validateAndPrettifyCustomPredicate()
|
||||
|
||||
if (!this.isPredicateJsonValid) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setIsSaving(true)
|
||||
|
||||
await this.application.mutator.changeAndSaveItem<SmartViewMutator>(this.view, (mutator) => {
|
||||
mutator.title = this.title
|
||||
mutator.iconString = this.icon || SmartViewDefaultIconName
|
||||
mutator.predicate = JSON.parse(this.predicateJson) as PredicateJsonForm
|
||||
})
|
||||
|
||||
this.setIsSaving(false)
|
||||
this.closeDialog()
|
||||
}
|
||||
|
||||
deleteView = async () => {
|
||||
if (!this.view) {
|
||||
return
|
||||
}
|
||||
const view = this.view
|
||||
|
||||
this.closeDialog()
|
||||
|
||||
const shouldDelete = await confirmDialog({
|
||||
text: STRING_DELETE_TAG,
|
||||
confirmButtonStyle: 'danger',
|
||||
})
|
||||
if (shouldDelete) {
|
||||
this.application.mutator.deleteItem(view).catch(console.error)
|
||||
}
|
||||
}
|
||||
|
||||
validateAndPrettifyCustomPredicate = () => {
|
||||
try {
|
||||
const parsedPredicate: PredicateJsonForm = JSON.parse(this.predicateJson)
|
||||
const predicate = predicateFromJson(parsedPredicate)
|
||||
|
||||
if (predicate) {
|
||||
this.setPredicateJson(JSON.stringify(parsedPredicate, null, 2))
|
||||
this.setIsPredicateJsonValid(true)
|
||||
} else {
|
||||
this.setIsPredicateJsonValid(false)
|
||||
}
|
||||
} catch (error) {
|
||||
this.setIsPredicateJsonValid(false)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
import Button from '@/Components/Button/Button'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import { SmartView } from '@standardnotes/snjs'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
type Props = {
|
||||
view: SmartView
|
||||
onEdit: () => void
|
||||
onDelete: () => void
|
||||
onDelete: (view: SmartView) => void
|
||||
}
|
||||
|
||||
const SmartViewItem = ({ view, onEdit, onDelete }: Props) => {
|
||||
const onClickDelete = useCallback(() => onDelete(view), [onDelete, view])
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2 py-1.5">
|
||||
<Icon type={view.iconString} size="custom" className="h-5.5 w-5.5" />
|
||||
@@ -16,7 +19,7 @@ const SmartViewItem = ({ view, onEdit, onDelete }: Props) => {
|
||||
<Button small onClick={onEdit}>
|
||||
Edit
|
||||
</Button>
|
||||
<Button small onClick={onDelete}>
|
||||
<Button small onClick={onClickDelete}>
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
import { isSystemView, SmartView } from '@standardnotes/snjs'
|
||||
import { ContentType, isSystemView, SmartView } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Title } from '../../../PreferencesComponents/Content'
|
||||
import PreferencesGroup from '../../../PreferencesComponents/PreferencesGroup'
|
||||
import PreferencesSegment from '../../../PreferencesComponents/PreferencesSegment'
|
||||
@@ -13,21 +12,45 @@ import EditSmartViewModal from './EditSmartViewModal'
|
||||
import SmartViewItem from './SmartViewItem'
|
||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||
import NoSubscriptionBanner from '@/Components/NoSubscriptionBanner/NoSubscriptionBanner'
|
||||
import { EditSmartViewModalController } from './EditSmartViewModalController'
|
||||
import { STRING_DELETE_TAG } from '@/Constants/Strings'
|
||||
import { confirmDialog } from '@standardnotes/ui-services'
|
||||
|
||||
type NewType = {
|
||||
application: WebApplication
|
||||
navigationController: NavigationController
|
||||
featuresController: FeaturesController
|
||||
}
|
||||
|
||||
type Props = NewType
|
||||
|
||||
const SmartViews = ({ application, navigationController, featuresController }: Props) => {
|
||||
const [editingSmartView, setEditingSmartView] = useState<SmartView | undefined>(undefined)
|
||||
|
||||
const SmartViews = ({ application, featuresController }: Props) => {
|
||||
const addSmartViewModalController = useMemo(() => new AddSmartViewModalController(application), [application])
|
||||
const editSmartViewModalController = useMemo(() => new EditSmartViewModalController(application), [application])
|
||||
|
||||
const nonSystemSmartViews = navigationController.smartViews.filter((view) => !isSystemView(view))
|
||||
const [smartViews, setSmartViews] = useState(() =>
|
||||
application.items.getSmartViews().filter((view) => !isSystemView(view)),
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const disposeItemStream = application.streamItems([ContentType.SmartView], () => {
|
||||
setSmartViews(application.items.getSmartViews().filter((view) => !isSystemView(view)))
|
||||
})
|
||||
|
||||
return disposeItemStream
|
||||
}, [application])
|
||||
|
||||
const deleteItem = useCallback(
|
||||
async (view: SmartView) => {
|
||||
const shouldDelete = await confirmDialog({
|
||||
text: STRING_DELETE_TAG,
|
||||
confirmButtonStyle: 'danger',
|
||||
})
|
||||
if (shouldDelete) {
|
||||
application.mutator.deleteItem(view).catch(console.error)
|
||||
}
|
||||
},
|
||||
[application.mutator],
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -45,12 +68,12 @@ const SmartViews = ({ application, navigationController, featuresController }: P
|
||||
{featuresController.hasSmartViews && (
|
||||
<>
|
||||
<div className="my-2 flex flex-col">
|
||||
{nonSystemSmartViews.map((view) => (
|
||||
{smartViews.map((view) => (
|
||||
<SmartViewItem
|
||||
key={view.uuid}
|
||||
view={view}
|
||||
onEdit={() => setEditingSmartView(view)}
|
||||
onDelete={() => navigationController.remove(view, true)}
|
||||
onEdit={() => editSmartViewModalController.setView(view)}
|
||||
onDelete={deleteItem}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -65,15 +88,8 @@ const SmartViews = ({ application, navigationController, featuresController }: P
|
||||
)}
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
{!!editingSmartView && (
|
||||
<EditSmartViewModal
|
||||
application={application}
|
||||
navigationController={navigationController}
|
||||
view={editingSmartView}
|
||||
closeDialog={() => {
|
||||
setEditingSmartView(undefined)
|
||||
}}
|
||||
/>
|
||||
{!!editSmartViewModalController.view && (
|
||||
<EditSmartViewModal controller={editSmartViewModalController} platform={application.platform} />
|
||||
)}
|
||||
{addSmartViewModalController.isAddingSmartView && (
|
||||
<AddSmartViewModal controller={addSmartViewModalController} platform={application.platform} />
|
||||
|
||||
@@ -8,7 +8,7 @@ import ModalDialogButtons from '@/Components/Shared/ModalDialogButtons'
|
||||
import ModalDialogDescription from '@/Components/Shared/ModalDialogDescription'
|
||||
import ModalDialogLabel from '@/Components/Shared/ModalDialogLabel'
|
||||
import Spinner from '@/Components/Spinner/Spinner'
|
||||
import { Platform } from '@standardnotes/snjs'
|
||||
import { Platform, SmartViewDefaultIconName } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { AddSmartViewModalController } from './AddSmartViewModalController'
|
||||
@@ -141,7 +141,7 @@ const AddSmartViewModal = ({ controller, platform }: Props) => {
|
||||
onClick={toggleIconPicker}
|
||||
ref={iconPickerButtonRef}
|
||||
>
|
||||
<Icon type={icon || 'restore'} />
|
||||
<Icon type={icon || SmartViewDefaultIconName} />
|
||||
</button>
|
||||
<Popover
|
||||
open={shouldShowIconPicker}
|
||||
@@ -152,9 +152,9 @@ const AddSmartViewModal = ({ controller, platform }: Props) => {
|
||||
>
|
||||
<div className="p-2">
|
||||
<IconPicker
|
||||
selectedValue={icon || 'restore'}
|
||||
selectedValue={icon || SmartViewDefaultIconName}
|
||||
onIconChange={(value?: string | undefined) => {
|
||||
setIcon(value ?? 'restore')
|
||||
setIcon(value ?? SmartViewDefaultIconName)
|
||||
toggleIconPicker()
|
||||
}}
|
||||
platform={platform}
|
||||
@@ -182,9 +182,9 @@ const AddSmartViewModal = ({ controller, platform }: Props) => {
|
||||
<TabPanel state={tabState} id="builder" className="flex flex-col gap-2.5 p-4">
|
||||
<CompoundPredicateBuilder controller={predicateController} />
|
||||
</TabPanel>
|
||||
<TabPanel state={tabState} id="custom">
|
||||
<TabPanel state={tabState} id="custom" className="flex flex-col">
|
||||
<textarea
|
||||
className="h-full min-h-[10rem] w-full resize-none bg-default py-1.5 px-2.5 font-mono text-sm"
|
||||
className="h-full min-h-[10rem] w-full flex-grow resize-none bg-default py-1.5 px-2.5 font-mono text-sm"
|
||||
value={customPredicateJson}
|
||||
onChange={(event) => {
|
||||
setCustomPredicateJson(event.target.value)
|
||||
@@ -194,7 +194,7 @@ const AddSmartViewModal = ({ controller, platform }: Props) => {
|
||||
ref={customJsonInputRef}
|
||||
/>
|
||||
{customPredicateJson && isCustomJsonValidPredicate === false && (
|
||||
<div className="mt-2 border-t border-border px-2.5 py-1.5 text-sm text-danger">
|
||||
<div className="border-t border-border px-2.5 py-1.5 text-sm text-danger">
|
||||
Invalid JSON. Double check your entry and try again.
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { CompoundPredicateBuilderController } from '@/Components/SmartViewBuilder/CompoundPredicateBuilderController'
|
||||
import { predicateFromJson, PredicateJsonForm } from '@standardnotes/snjs'
|
||||
import { predicateFromJson, PredicateJsonForm, SmartViewDefaultIconName } from '@standardnotes/snjs'
|
||||
import { action, makeObservable, observable } from 'mobx'
|
||||
|
||||
export class AddSmartViewModalController {
|
||||
@@ -9,7 +9,7 @@ export class AddSmartViewModalController {
|
||||
|
||||
title = ''
|
||||
|
||||
icon = 'restore'
|
||||
icon: string = SmartViewDefaultIconName
|
||||
|
||||
predicateController = new CompoundPredicateBuilderController()
|
||||
|
||||
|
||||
@@ -135,7 +135,11 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
|
||||
'md:hover:[overflow-y:_overlay]',
|
||||
)}
|
||||
>
|
||||
<SmartViewsSection application={application} viewControllerManager={viewControllerManager} />
|
||||
<SmartViewsSection
|
||||
application={application}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
/>
|
||||
<TagsSection viewControllerManager={viewControllerManager} />
|
||||
</div>
|
||||
{NavigationFooter}
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
import { SmartView } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent } from 'react'
|
||||
import SmartViewsListItem from './SmartViewsListItem'
|
||||
|
||||
type Props = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
navigationController: NavigationController
|
||||
featuresController: FeaturesController
|
||||
setEditingSmartView: (smartView: SmartView) => void
|
||||
}
|
||||
|
||||
const SmartViewsList: FunctionComponent<Props> = ({ viewControllerManager, setEditingSmartView }: Props) => {
|
||||
const allViews = viewControllerManager.navigationController.smartViews
|
||||
const SmartViewsList: FunctionComponent<Props> = ({
|
||||
navigationController,
|
||||
featuresController,
|
||||
setEditingSmartView,
|
||||
}: Props) => {
|
||||
const allViews = navigationController.smartViews
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -19,8 +25,8 @@ const SmartViewsList: FunctionComponent<Props> = ({ viewControllerManager, setEd
|
||||
<SmartViewsListItem
|
||||
key={view.uuid}
|
||||
view={view}
|
||||
tagsState={viewControllerManager.navigationController}
|
||||
features={viewControllerManager.featuresController}
|
||||
tagsState={navigationController}
|
||||
features={featuresController}
|
||||
setEditingSmartView={setEditingSmartView}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -1,35 +1,36 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { SMART_TAGS_FEATURE_NAME } from '@/Constants/Constants'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
import { usePremiumModal } from '@/Hooks/usePremiumModal'
|
||||
import { SmartView } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useCallback, useMemo, useState } from 'react'
|
||||
import { FunctionComponent, useCallback, useMemo } from 'react'
|
||||
import IconButton from '../Button/IconButton'
|
||||
import EditSmartViewModal from '../Preferences/Panes/General/SmartViews/EditSmartViewModal'
|
||||
import { EditSmartViewModalController } from '../Preferences/Panes/General/SmartViews/EditSmartViewModalController'
|
||||
import AddSmartViewModal from '../SmartViewBuilder/AddSmartViewModal'
|
||||
import { AddSmartViewModalController } from '../SmartViewBuilder/AddSmartViewModalController'
|
||||
import SmartViewsList from './SmartViewsList'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
navigationController: NavigationController
|
||||
featuresController: FeaturesController
|
||||
}
|
||||
|
||||
const SmartViewsSection: FunctionComponent<Props> = ({ application, viewControllerManager }) => {
|
||||
const SmartViewsSection: FunctionComponent<Props> = ({ application, navigationController, featuresController }) => {
|
||||
const premiumModal = usePremiumModal()
|
||||
const addSmartViewModalController = useMemo(() => new AddSmartViewModalController(application), [application])
|
||||
|
||||
const [editingSmartView, setEditingSmartView] = useState<SmartView | undefined>(undefined)
|
||||
const editSmartViewModalController = useMemo(() => new EditSmartViewModalController(application), [application])
|
||||
|
||||
const createNewSmartView = useCallback(() => {
|
||||
if (!viewControllerManager.featuresController.hasSmartViews) {
|
||||
if (!featuresController.hasSmartViews) {
|
||||
premiumModal.activate(SMART_TAGS_FEATURE_NAME)
|
||||
return
|
||||
}
|
||||
|
||||
addSmartViewModalController.setIsAddingSmartView(true)
|
||||
}, [addSmartViewModalController, premiumModal, viewControllerManager.featuresController.hasSmartViews])
|
||||
}, [addSmartViewModalController, premiumModal, featuresController.hasSmartViews])
|
||||
|
||||
return (
|
||||
<section>
|
||||
@@ -47,16 +48,13 @@ const SmartViewsSection: FunctionComponent<Props> = ({ application, viewControll
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SmartViewsList viewControllerManager={viewControllerManager} setEditingSmartView={setEditingSmartView} />
|
||||
{!!editingSmartView && (
|
||||
<EditSmartViewModal
|
||||
application={application}
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
view={editingSmartView}
|
||||
closeDialog={() => {
|
||||
setEditingSmartView(undefined)
|
||||
}}
|
||||
/>
|
||||
<SmartViewsList
|
||||
navigationController={navigationController}
|
||||
featuresController={featuresController}
|
||||
setEditingSmartView={editSmartViewModalController.setView}
|
||||
/>
|
||||
{!!editSmartViewModalController.view && (
|
||||
<EditSmartViewModal controller={editSmartViewModalController} platform={application.platform} />
|
||||
)}
|
||||
{addSmartViewModalController.isAddingSmartView && (
|
||||
<AddSmartViewModal controller={addSmartViewModalController} platform={application.platform} />
|
||||
|
||||
@@ -119,7 +119,7 @@ export class ItemListController extends AbstractViewController implements Intern
|
||||
)
|
||||
|
||||
this.disposers.push(
|
||||
application.streamItems<SNTag>([ContentType.Tag], async ({ changed, inserted }) => {
|
||||
application.streamItems<SNTag>([ContentType.Tag, ContentType.SmartView], async ({ changed, inserted }) => {
|
||||
const tags = [...changed, ...inserted]
|
||||
|
||||
const { didReloadItems } = await this.reloadDisplayPreferences()
|
||||
|
||||
Reference in New Issue
Block a user