internal: incomplete vault systems behind feature flag (#2340)

This commit is contained in:
Mo
2023-06-30 09:01:56 -05:00
committed by GitHub
parent d16e401bb9
commit b032eb9c9b
638 changed files with 20321 additions and 4813 deletions

View File

@@ -0,0 +1,46 @@
import { FunctionComponent } from 'react'
import Icon from '../Icon/Icon'
import { useApplication } from '../ApplicationProvider'
import { DecryptedItemInterface } from '@standardnotes/snjs'
import { featureTrunkVaultsEnabled } from '@/FeatureTrunk'
type Props = {
item: DecryptedItemInterface
}
const CollaborationInfoHUD: FunctionComponent<Props> = ({ item }) => {
const application = useApplication()
if (!featureTrunkVaultsEnabled()) {
return null
}
if (application.items.isTemplateItem(item)) {
return null
}
const vault = application.vaults.getItemVault(item)
if (!vault) {
return null
}
const lastEditedBy = application.sharedVaults.getItemLastEditedBy(item)
return (
<div className="flex flex-wrap items-start gap-2">
<div title="Vault name" className={'flex rounded bg-success py-1 px-1.5 text-success-contrast'}>
<Icon ariaLabel="Shared in vault" type="safe-square" className="mr-1 text-info-contrast" size="medium" />
<span className="mr-auto overflow-hidden text-ellipsis text-xs">{vault.name}</span>
</div>
{lastEditedBy && (
<div title="Last edited by" className={'flex rounded bg-info py-1 px-1.5 text-info-contrast'}>
<Icon ariaLabel="Shared by" type="pencil" className="mr-1 text-info-contrast" size="medium" />
<span className="mr-auto overflow-hidden text-ellipsis text-xs">{lastEditedBy?.name}</span>
</div>
)}
</div>
)
}
export default CollaborationInfoHUD

View File

@@ -5,10 +5,11 @@ import {
SNComponentManager,
SNComponent,
SNTag,
ItemsClientInterface,
SNNote,
Deferred,
SyncServiceInterface,
ItemManagerInterface,
MutatorClientInterface,
} from '@standardnotes/snjs'
import { FeatureIdentifier, NoteType } from '@standardnotes/features'
import { NoteViewController } from './NoteViewController'
@@ -24,7 +25,9 @@ describe('note view controller', () => {
application.noAccount = jest.fn().mockReturnValue(false)
application.isNativeMobileWeb = jest.fn().mockReturnValue(false)
Object.defineProperty(application, 'items', { value: {} as jest.Mocked<ItemsClientInterface> })
const items = {} as jest.Mocked<ItemManagerInterface>
items.createTemplateItem = jest.fn().mockReturnValue({} as SNNote)
Object.defineProperty(application, 'items', { value: items })
Object.defineProperty(application, 'sync', { value: {} as jest.Mocked<SyncServiceInterface> })
application.sync.sync = jest.fn().mockReturnValue(Promise.resolve())
@@ -33,8 +36,7 @@ describe('note view controller', () => {
componentManager.legacyGetDefaultEditor = jest.fn()
Object.defineProperty(application, 'componentManager', { value: componentManager })
const mutator = {} as jest.Mocked<MutatorService>
mutator.createTemplateItem = jest.fn().mockReturnValue({} as SNNote)
const mutator = {} as jest.Mocked<MutatorClientInterface>
Object.defineProperty(application, 'mutator', { value: mutator })
})
@@ -44,7 +46,7 @@ describe('note view controller', () => {
const controller = new NoteViewController(application)
await controller.initialize()
expect(application.mutator.createTemplateItem).toHaveBeenCalledWith(
expect(application.items.createTemplateItem).toHaveBeenCalledWith(
ContentType.Note,
expect.objectContaining({ noteType: NoteType.Plain }),
expect.anything(),
@@ -65,7 +67,7 @@ describe('note view controller', () => {
const controller = new NoteViewController(application)
await controller.initialize()
expect(application.mutator.createTemplateItem).toHaveBeenCalledWith(
expect(application.items.createTemplateItem).toHaveBeenCalledWith(
ContentType.Note,
expect.objectContaining({ noteType: NoteType.Markdown }),
expect.anything(),
@@ -80,13 +82,13 @@ describe('note view controller', () => {
} as jest.Mocked<SNTag>
application.items.findItem = jest.fn().mockReturnValue(tag)
application.items.addTagToNote = jest.fn()
application.mutator.addTagToNote = jest.fn()
const controller = new NoteViewController(application, undefined, { tag: tag.uuid })
await controller.initialize()
expect(controller['defaultTag']).toEqual(tag)
expect(application.items.addTagToNote).toHaveBeenCalledWith(expect.anything(), tag, expect.anything())
expect(application.mutator.addTagToNote).toHaveBeenCalledWith(expect.anything(), tag, expect.anything())
})
it('should wait until item finishes saving locally before deiniting', async () => {

View File

@@ -1,6 +1,14 @@
import { WebApplication } from '@/Application/WebApplication'
import { noteTypeForEditorIdentifier } from '@standardnotes/features'
import { SNNote, SNTag, NoteContent, DecryptedItemInterface, PayloadEmitSource, PrefKey } from '@standardnotes/models'
import {
SNNote,
SNTag,
NoteContent,
DecryptedItemInterface,
PayloadEmitSource,
PrefKey,
PayloadVaultOverrides,
} from '@standardnotes/models'
import { UuidString } from '@standardnotes/snjs'
import { removeFromArray } from '@standardnotes/utils'
import { ContentType } from '@standardnotes/common'
@@ -90,7 +98,7 @@ export class NoteViewController implements ItemViewControllerInterface {
const noteType = noteTypeForEditorIdentifier(editorIdentifier)
const note = this.application.mutator.createTemplateItem<NoteContent, SNNote>(
const note = this.application.items.createTemplateItem<NoteContent, SNNote>(
ContentType.Note,
{
text: '',
@@ -101,6 +109,7 @@ export class NoteViewController implements ItemViewControllerInterface {
},
{
created_at: this.templateNoteOptions?.createdAt || new Date(),
...PayloadVaultOverrides(this.templateNoteOptions?.vault),
},
)
@@ -110,7 +119,7 @@ export class NoteViewController implements ItemViewControllerInterface {
if (this.defaultTagUuid) {
const tag = this.application.items.findItem(this.defaultTagUuid) as SNTag
await this.application.items.addTagToNote(note, tag, addTagHierarchy)
await this.application.mutator.addTagToNote(note, tag, addTagHierarchy)
}
this.notifyObservers(this.item, PayloadEmitSource.InitialObserverRegistrationPush)

View File

@@ -1,8 +1,9 @@
import { UuidString } from '@standardnotes/snjs'
import { UuidString, VaultListingInterface } from '@standardnotes/snjs'
export type TemplateNoteViewControllerOptions = {
title?: string
tag?: UuidString
vault?: VaultListingInterface
createdAt?: Date
autofocusBehavior?: TemplateNoteViewAutofocusBehavior
}

View File

@@ -65,12 +65,13 @@ const NoteConflictResolutionModal = ({
async (note: SNNote) => {
await application.mutator
.deleteItem(note)
.then(() => application.sync.sync())
.catch(console.error)
.then(() => {
setSelectedVersions([allVersions[0].uuid])
})
},
[allVersions, application.mutator],
[allVersions, application.mutator, application.sync],
)
const [selectedAction, setSelectionAction] = useState<ConflictAction>('move-to-trash')

View File

@@ -45,6 +45,7 @@ import { SuperEditorContentId } from '../SuperEditor/Constants'
import { NoteViewController } from './Controller/NoteViewController'
import { PlainEditor, PlainEditorInterface } from './PlainEditor/PlainEditor'
import { EditorMargins, EditorMaxWidths } from '../EditorWidthSelectionModal/EditorWidths'
import CollaborationInfoHUD from './CollaborationInfoHUD'
import Button from '../Button/Button'
import ModalOverlay from '../Modal/ModalOverlay'
import NoteConflictResolutionModal from './NoteConflictResolutionModal/NoteConflictResolutionModal'
@@ -74,7 +75,7 @@ type State = {
monospaceFont?: boolean
plainEditorFocused?: boolean
paneGestureEnabled?: boolean
noteLastEditedByUuid?: string
updateSavingIndicator?: boolean
editorFeatureIdentifier?: string
noteType?: NoteType
@@ -270,6 +271,12 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
})
}
if (note.last_edited_by_uuid !== this.state.noteLastEditedByUuid) {
this.setState({
noteLastEditedByUuid: note.last_edited_by_uuid,
})
}
if (note.locked !== this.state.noteLocked) {
this.setState({
noteLocked: note.locked,
@@ -651,7 +658,10 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
}
performNoteDeletion(note: SNNote) {
this.application.mutator.deleteItem(note).catch(console.error)
this.application.mutator
.deleteItem(note)
.then(() => this.application.sync.sync())
.catch(console.error)
}
onPanelResizeFinish = async (width: number, left: number, isMaxWidth: boolean) => {
@@ -897,6 +907,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
)}
{renderHeaderOptions && (
<div className="note-view-options-buttons flex items-center gap-3">
<CollaborationInfoHUD item={this.note} />
<LinkedItemsButton
filesController={this.viewControllerManager.filesController}
linkingController={this.viewControllerManager.linkingController}

View File

@@ -24,7 +24,7 @@ const NoteViewFileDropTarget = ({ note, linkingController, noteViewElement, file
tooltipText: 'Drop your files to upload and link them to the current note',
callback: async (uploadedFile) => {
await linkingController.linkItems(note, uploadedFile)
void application.mutator.changeAndSaveItem(uploadedFile, (mutator) => {
void application.changeAndSaveItem(uploadedFile, (mutator) => {
mutator.protected = note.protected
})
filesController.notifyObserversOfUploadedFileLinkingToCurrentNote(uploadedFile.uuid)
@@ -37,7 +37,7 @@ const NoteViewFileDropTarget = ({ note, linkingController, noteViewElement, file
removeDragTarget(target)
}
}
}, [addDragTarget, linkingController, note, noteViewElement, removeDragTarget, filesController, application.mutator])
}, [addDragTarget, linkingController, note, noteViewElement, removeDragTarget, filesController, application])
return isDraggingFiles ? (
// Required to block drag events to editor iframe

View File

@@ -36,14 +36,14 @@ export const ReadonlyNoteContent = ({
return undefined
}
const templateNoteForRevision = application.mutator.createTemplateItem(ContentType.Note, note.content) as SNNote
const templateNoteForRevision = application.items.createTemplateItem(ContentType.Note, note.content) as SNNote
const componentViewer = application.componentManager.createComponentViewer(editorForCurrentNote)
componentViewer.setReadonly(true)
componentViewer.lockReadonly = true
componentViewer.overrideContextItem = templateNoteForRevision
return componentViewer
}, [application.componentManager, application.mutator, note])
}, [application.componentManager, application.items, note])
useEffect(() => {
return () => {