feat: You can now select an existing tag to automatically add imported notes to (#2663)

This commit is contained in:
Aman Harwara
2023-11-29 22:28:04 +05:30
committed by GitHub
parent c9c3c394e5
commit eb75329fb4
5 changed files with 159 additions and 26 deletions

View File

@@ -48,6 +48,9 @@ export const PrefDefaults = {
[PrefKey.ActiveThemes]: [],
[PrefKey.ActiveComponents]: [],
[PrefKey.AlwaysShowSuperToolbar]: true,
[PrefKey.AddImportsToTag]: true,
[PrefKey.AlwaysCreateNewTagForImports]: true,
[PrefKey.ExistingTagForImports]: undefined,
} satisfies {
[key in PrefKey]: PrefValue[key]
}

View File

@@ -49,6 +49,9 @@ export enum PrefKey {
ActiveThemes = 'activeThemes',
ActiveComponents = 'activeComponents',
AlwaysShowSuperToolbar = 'alwaysShowSuperToolbar',
AddImportsToTag = 'addImportsToTag',
AlwaysCreateNewTagForImports = 'alwaysCreateNewTagForImports',
ExistingTagForImports = 'existingTagForImports',
}
export type PrefValue = {
@@ -93,4 +96,7 @@ export type PrefValue = {
[PrefKey.ActiveThemes]: string[]
[PrefKey.ActiveComponents]: string[]
[PrefKey.AlwaysShowSuperToolbar]: boolean
[PrefKey.AddImportsToTag]: boolean
[PrefKey.AlwaysCreateNewTagForImports]: boolean
[PrefKey.ExistingTagForImports]: string | undefined
}

View File

@@ -391,6 +391,9 @@ export class WebDependencies extends DependencyContainer {
this.get<NavigationController>(Web_TYPES.NavigationController),
application.items,
application.mutator,
this.get<LinkingController>(Web_TYPES.LinkingController),
application.preferences,
application.events,
)
})

View File

@@ -7,6 +7,10 @@ import ModalOverlay from '../Modal/ModalOverlay'
import { ImportModalController } from '@/Components/ImportModal/ImportModalController'
import { useApplication } from '../ApplicationProvider'
import Switch from '../Switch/Switch'
import LinkedItemBubble from '../LinkedItems/LinkedItemBubble'
import { createLinkFromItem } from '@/Utils/Items/Search/createLinkFromItem'
import ItemSelectionDropdown from '../ItemSelectionDropdown/ItemSelectionDropdown'
import { ContentType, SNTag } from '@standardnotes/snjs'
const ImportModal = ({ importModalController }: { importModalController: ImportModalController }) => {
const application = useApplication()
@@ -14,8 +18,12 @@ const ImportModal = ({ importModalController }: { importModalController: ImportM
const {
files,
setFiles,
addImportsToTag,
setAddImportsToTag,
shouldCreateTag,
setShouldCreateTag,
existingTagForImports,
setExistingTagForImports,
updateFile,
removeFile,
parseAndImport,
@@ -35,6 +43,7 @@ const ImportModal = ({ importModalController }: { importModalController: ImportM
onClick: parseAndImport,
hidden: !isReadyToImport,
mobileSlot: 'right',
disabled: !isReadyToImport || (!shouldCreateTag && !existingTagForImports),
},
{
label: importSuccessOrError ? 'Close' : 'Cancel',
@@ -43,13 +52,13 @@ const ImportModal = ({ importModalController }: { importModalController: ImportM
mobileSlot: 'left',
},
],
[close, importSuccessOrError, isReadyToImport, parseAndImport],
[close, existingTagForImports, importSuccessOrError, isReadyToImport, parseAndImport, shouldCreateTag],
)
return (
<ModalOverlay isOpen={isVisible} close={close}>
<Modal title="Import" close={close} actions={modalActions}>
<div className="px-4 py-4">
<Modal title="Import" close={close} actions={modalActions} className="flex flex-col">
<div className="min-h-0 flex-grow px-4 py-4">
{!files.length && <ImportModalInitialPage setFiles={setFiles} />}
{files.length > 0 && (
<div className="divide-y divide-border">
@@ -66,10 +75,63 @@ const ImportModal = ({ importModalController }: { importModalController: ImportM
)}
</div>
{files.length > 0 && (
<label className="flex items-center gap-2 border-t border-border px-4 py-2">
<Switch checked={shouldCreateTag} onChange={setShouldCreateTag} />
<span className="text-sm">Create tag with all imported notes</span>
</label>
<div className="flex flex-col gap-3 border-t border-border px-4 py-4 md:gap-2 md:py-3">
<Switch className="flex items-center gap-2" checked={addImportsToTag} onChange={setAddImportsToTag}>
<span className="text-sm">Add all imported notes to tag</span>
</Switch>
{addImportsToTag && (
<>
<label className="mt-1.5 flex items-center gap-2 text-sm">
<input
type="radio"
name="import-tag"
className="h-6 w-6 md:h-4 md:w-4"
checked={shouldCreateTag}
onChange={() => {
setShouldCreateTag(true)
}}
/>
Create new tag
</label>
<div className="flex flex-col gap-2">
<div className="flex items-center gap-2">
<label className="flex items-center gap-2 text-sm">
<input
type="radio"
name="import-tag"
className="h-6 w-6 md:h-4 md:w-4"
checked={!shouldCreateTag}
onChange={() => {
setShouldCreateTag(false)
}}
/>
Add to existing tag
</label>
{existingTagForImports && (
<LinkedItemBubble
className="m-1 mr-2"
link={createLinkFromItem(existingTagForImports, 'linked')}
unlinkItem={async () => {
setExistingTagForImports(undefined)
}}
isBidirectional={false}
inlineFlex={true}
/>
)}
</div>
{!shouldCreateTag && (
<div className="ml-8 md:ml-6">
<ItemSelectionDropdown
onSelection={(tag) => setExistingTagForImports(tag as SNTag)}
placeholder="Select tag to add imported notes to..."
contentTypes={[ContentType.TYPES.Tag]}
/>
</div>
)}
</div>
</>
)}
</div>
)}
</Modal>
</ModalOverlay>

View File

@@ -1,14 +1,19 @@
import { DecryptedTransferPayload, SNTag, TagContent } from '@standardnotes/models'
import { DecryptedTransferPayload, PrefDefaults, PrefKey, SNNote, SNTag, TagContent } from '@standardnotes/models'
import {
ContentType,
InternalEventBusInterface,
ItemManagerInterface,
MutatorClientInterface,
pluralize,
PreferenceServiceInterface,
PreferencesServiceEvent,
UuidGenerator,
} from '@standardnotes/snjs'
import { Importer, NoteImportType } from '@standardnotes/ui-services'
import { action, makeObservable, observable } from 'mobx'
import { action, makeObservable, observable, runInAction } from 'mobx'
import { NavigationController } from '../../Controllers/Navigation/NavigationController'
import { LinkingController } from '@/Controllers/LinkingController'
import { AbstractViewController } from '@/Controllers/Abstract/AbstractViewController'
type ImportModalFileCommon = {
id: string
@@ -27,9 +32,11 @@ export type ImportModalFile = (
) &
ImportModalFileCommon
export class ImportModalController {
export class ImportModalController extends AbstractViewController {
isVisible = false
shouldCreateTag = false
addImportsToTag = false
shouldCreateTag = true
existingTagForImports: SNTag | undefined = undefined
files: ImportModalFile[] = []
importTag: SNTag | undefined = undefined
@@ -38,14 +45,25 @@ export class ImportModalController {
private navigationController: NavigationController,
private items: ItemManagerInterface,
private mutator: MutatorClientInterface,
private linkingController: LinkingController,
private preferences: PreferenceServiceInterface,
eventBus: InternalEventBusInterface,
) {
super(eventBus)
makeObservable(this, {
isVisible: observable,
setIsVisible: action,
addImportsToTag: observable,
setAddImportsToTag: action,
shouldCreateTag: observable,
setShouldCreateTag: action,
existingTagForImports: observable,
setExistingTagForImports: action,
files: observable,
setFiles: action,
updateFile: action,
@@ -54,14 +72,38 @@ export class ImportModalController {
importTag: observable,
setImportTag: action,
})
this.disposers.push(
preferences.addEventObserver((event) => {
if (event === PreferencesServiceEvent.PreferencesChanged) {
runInAction(() => {
this.addImportsToTag = preferences.getValue(PrefKey.AddImportsToTag, PrefDefaults[PrefKey.AddImportsToTag])
this.shouldCreateTag = preferences.getValue(
PrefKey.AlwaysCreateNewTagForImports,
PrefDefaults[PrefKey.AlwaysCreateNewTagForImports],
)
const existingTagUuid = preferences.getValue(PrefKey.ExistingTagForImports)
this.existingTagForImports = existingTagUuid ? this.items.findItem(existingTagUuid) : undefined
})
}
}),
)
}
setIsVisible = (isVisible: boolean) => {
this.isVisible = isVisible
}
setAddImportsToTag = (addImportsToTag: boolean) => {
this.preferences.setValue(PrefKey.AddImportsToTag, addImportsToTag).catch(console.error)
}
setShouldCreateTag = (shouldCreateTag: boolean) => {
this.shouldCreateTag = shouldCreateTag
this.preferences.setValue(PrefKey.AlwaysCreateNewTagForImports, shouldCreateTag).catch(console.error)
}
setExistingTagForImports = (tag: SNTag | undefined) => {
this.preferences.setValue(PrefKey.ExistingTagForImports, tag?.uuid).catch(console.error)
}
setFiles = (files: File[], service?: NoteImportType) => {
@@ -87,7 +129,6 @@ export class ImportModalController {
close = () => {
this.setIsVisible(false)
this.setShouldCreateTag(false)
if (this.importTag) {
this.navigationController
.setSelectedTag(this.importTag, 'all', {
@@ -175,20 +216,38 @@ export class ImportModalController {
if (!importedPayloads.length) {
return
}
if (this.shouldCreateTag) {
if (this.addImportsToTag) {
const currentDate = new Date()
const importTagItem = this.items.createTemplateItem<TagContent, SNTag>(ContentType.TYPES.Tag, {
title: `Imported on ${currentDate.toLocaleString()}`,
expanded: false,
iconString: '',
references: importedPayloads
.filter((payload) => payload.content_type === ContentType.TYPES.Note)
.map((payload) => ({
content_type: ContentType.TYPES.Note,
uuid: payload.uuid,
})),
})
const importTag = await this.mutator.insertItem(importTagItem)
let importTag: SNTag | undefined
if (this.shouldCreateTag) {
const importTagItem = this.items.createTemplateItem<TagContent, SNTag>(ContentType.TYPES.Tag, {
title: `Imported on ${currentDate.toLocaleString()}`,
expanded: false,
iconString: '',
references: importedPayloads
.filter((payload) => payload.content_type === ContentType.TYPES.Note)
.map((payload) => ({
content_type: ContentType.TYPES.Note,
uuid: payload.uuid,
})),
})
importTag = await this.mutator.insertItem<SNTag>(importTagItem)
} else if (this.existingTagForImports) {
try {
const latestExistingTag = this.items.findSureItem<SNTag>(this.existingTagForImports.uuid)
await Promise.all(
importedPayloads
.filter((payload) => payload.content_type === ContentType.TYPES.Note)
.map(async (payload) => {
const note = this.items.findSureItem<SNNote>(payload.uuid)
await this.linkingController.addTagToItem(latestExistingTag, note)
}),
)
importTag = this.items.findSureItem<SNTag>(this.existingTagForImports.uuid)
} catch (error) {
console.error(error)
}
}
if (importTag) {
this.setImportTag(importTag as SNTag)
}