Editor TypeScript
This commit is contained in:
@@ -43,6 +43,10 @@ export class PureCtrl {
|
||||
this.deinit();
|
||||
}
|
||||
|
||||
public get appState() {
|
||||
return this.application!.getAppState();
|
||||
}
|
||||
|
||||
/** @private */
|
||||
async resetState() {
|
||||
this.state = this.getInitialState();
|
||||
@@ -66,6 +70,10 @@ export class PureCtrl {
|
||||
});
|
||||
}
|
||||
|
||||
async updateUI(func: () => void) {
|
||||
this.$timeout(func);
|
||||
}
|
||||
|
||||
initProps(props: CtrlProps) {
|
||||
if (Object.keys(this.props).length > 0) {
|
||||
throw 'Already init-ed props.';
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -157,10 +157,10 @@ class FooterCtrl extends PureCtrl {
|
||||
|
||||
streamItems() {
|
||||
this.application.streamItems({
|
||||
contentType: ContentTypes.Component,
|
||||
contentType: ContentType.Component,
|
||||
stream: async () => {
|
||||
this.rooms = this.application.getItems({
|
||||
contentType: ContentTypes.Component
|
||||
contentType: ContentType.Component
|
||||
}).filter((candidate) => {
|
||||
return candidate.area === 'rooms' && !candidate.deleted;
|
||||
});
|
||||
@@ -175,7 +175,7 @@ class FooterCtrl extends PureCtrl {
|
||||
contentType: 'SN|Theme',
|
||||
stream: async () => {
|
||||
const themes = this.application.getDisplayableItems({
|
||||
contentType: ContentTypes.Theme
|
||||
contentType: ContentType.Theme
|
||||
}).filter((candidate) => {
|
||||
return (
|
||||
!candidate.deleted &&
|
||||
|
||||
@@ -3,7 +3,7 @@ import template from '%/notes.pug';
|
||||
import { ApplicationEvent, ContentTypes, removeFromArray } from 'snjs';
|
||||
import { PureCtrl } from '@Controllers';
|
||||
import { AppStateEvent } from '@/services/state';
|
||||
import { KeyboardModifiers, KeyboardKeys } from '@/services/keyboardManager';
|
||||
import { KeyboardModifier, KeyboardKey } from '@/services/keyboardManager';
|
||||
import {
|
||||
PrefKeys
|
||||
} from '@/services/preferencesManager';
|
||||
@@ -155,7 +155,7 @@ class NotesCtrl extends PureCtrl {
|
||||
|
||||
streamNotesAndTags() {
|
||||
this.application.streamItems({
|
||||
contentType: [ContentTypes.Note, ContentTypes.Tag],
|
||||
contentType: [ContentType.Note, ContentType.Tag],
|
||||
stream: async ({ items }) => {
|
||||
await this.reloadNotes();
|
||||
const selectedNote = this.state.selectedNote;
|
||||
@@ -169,7 +169,7 @@ class NotesCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
/** Note has changed values, reset its flags */
|
||||
const notes = items.filter((item) => item.content_type === ContentTypes.Note);
|
||||
const notes = items.filter((item) => item.content_type === ContentType.Note);
|
||||
for (const note of notes) {
|
||||
if(note.deleted) {
|
||||
continue;
|
||||
@@ -202,7 +202,7 @@ class NotesCtrl extends PureCtrl {
|
||||
title = `Note ${this.state.notes.length + 1}`;
|
||||
}
|
||||
const newNote = await this.application.createManagedItem({
|
||||
contentType: ContentTypes.Note,
|
||||
contentType: ContentType.Note,
|
||||
content: {
|
||||
text: '',
|
||||
title: title
|
||||
@@ -680,8 +680,8 @@ class NotesCtrl extends PureCtrl {
|
||||
this.newNoteKeyObserver = this.application.getKeyboardService().addKeyObserver({
|
||||
key: 'n',
|
||||
modifiers: [
|
||||
KeyboardModifiers.Meta,
|
||||
KeyboardModifiers.Ctrl
|
||||
KeyboardModifier.Meta,
|
||||
KeyboardModifier.Ctrl
|
||||
],
|
||||
onKeyDown: (event) => {
|
||||
event.preventDefault();
|
||||
@@ -690,7 +690,7 @@ class NotesCtrl extends PureCtrl {
|
||||
});
|
||||
|
||||
this.nextNoteKeyObserver = this.application.getKeyboardService().addKeyObserver({
|
||||
key: KeyboardKeys.Down,
|
||||
key: KeyboardKey.Down,
|
||||
elements: [
|
||||
document.body,
|
||||
this.getSearchBar()
|
||||
@@ -705,7 +705,7 @@ class NotesCtrl extends PureCtrl {
|
||||
});
|
||||
|
||||
this.nextNoteKeyObserver = this.application.getKeyboardService().addKeyObserver({
|
||||
key: KeyboardKeys.Up,
|
||||
key: KeyboardKey.Up,
|
||||
element: document.body,
|
||||
onKeyDown: (event) => {
|
||||
this.selectPreviousNote();
|
||||
@@ -715,8 +715,8 @@ class NotesCtrl extends PureCtrl {
|
||||
this.searchKeyObserver = this.application.getKeyboardService().addKeyObserver({
|
||||
key: "f",
|
||||
modifiers: [
|
||||
KeyboardModifiers.Meta,
|
||||
KeyboardModifiers.Shift
|
||||
KeyboardModifier.Meta,
|
||||
KeyboardModifier.Shift
|
||||
],
|
||||
onKeyDown: (event) => {
|
||||
const searchBar = this.getSearchBar();
|
||||
|
||||
@@ -1,21 +1,40 @@
|
||||
import { WebDirective, PanelPuppet } from './../types';
|
||||
import { WebApplication } from './../application';
|
||||
import {
|
||||
SNNote,
|
||||
SNSmartTag,
|
||||
ContentTypes,
|
||||
SNTag,
|
||||
ContentType,
|
||||
ApplicationEvent,
|
||||
ComponentActions
|
||||
ComponentAction,
|
||||
SNSmartTag,
|
||||
ComponentArea,
|
||||
SNComponent
|
||||
} from 'snjs';
|
||||
import template from '%/tags.pug';
|
||||
import { AppStateEvent } from '@/services/state';
|
||||
import { PANEL_NAME_TAGS } from '@/controllers/constants';
|
||||
import { PrefKeys } from '@/services/preferencesManager';
|
||||
import { STRING_DELETE_TAG } from '@/strings';
|
||||
import { PureCtrl } from '@Controllers';
|
||||
import { PureCtrl } from '@Controllers/abstract/pure_ctrl';
|
||||
import { UuidString } from '@/../../../../snjs/dist/@types/types';
|
||||
import { TagMutator } from '@/../../../../snjs/dist/@types/models/app/tag';
|
||||
|
||||
type NoteCounts = Partial<Record<string, number>>
|
||||
|
||||
class TagsPanelCtrl extends PureCtrl {
|
||||
|
||||
/** Passed through template */
|
||||
readonly application!: WebApplication
|
||||
private readonly panelPuppet: PanelPuppet
|
||||
private unregisterComponent?: any
|
||||
component?: SNComponent
|
||||
private editingOriginalName?: string
|
||||
formData: { tagTitle?: string } = {}
|
||||
titles: Partial<Record<UuidString, string>> = {}
|
||||
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
$timeout,
|
||||
$timeout: ng.ITimeoutService,
|
||||
) {
|
||||
super($timeout);
|
||||
this.panelPuppet = {
|
||||
@@ -25,7 +44,7 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
|
||||
deinit() {
|
||||
this.unregisterComponent();
|
||||
this.unregisterComponent = null;
|
||||
this.unregisterComponent = undefined;
|
||||
super.deinit();
|
||||
}
|
||||
|
||||
@@ -37,12 +56,12 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
};
|
||||
}
|
||||
|
||||
onAppStart() {
|
||||
async onAppStart() {
|
||||
super.onAppStart();
|
||||
this.registerComponentHandler();
|
||||
}
|
||||
|
||||
onAppLaunch() {
|
||||
async onAppLaunch() {
|
||||
super.onAppLaunch();
|
||||
this.loadPreferences();
|
||||
this.beginStreamingItems();
|
||||
@@ -64,20 +83,21 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
* @access private
|
||||
*/
|
||||
getMappedTags() {
|
||||
const tags = this.application.getItems({ contentType: ContentTypes.Tag });
|
||||
const tags = this.application.getItems(ContentType.Tag) as SNTag[];
|
||||
return tags.sort((a, b) => {
|
||||
return a.content.title < b.content.title ? -1 : 1;
|
||||
return a.title < b.title ? -1 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
beginStreamingItems() {
|
||||
this.application.streamItems({
|
||||
contentType: ContentTypes.Tag,
|
||||
stream: async ({ items }) => {
|
||||
this.application.streamItems(
|
||||
ContentType.Tag,
|
||||
async (items) => {
|
||||
await this.setState({
|
||||
tags: this.getMappedTags(),
|
||||
smartTags: this.application.getSmartTags(),
|
||||
});
|
||||
this.reloadTitles(items as SNTag[]);
|
||||
this.reloadNoteCounts();
|
||||
if (this.state.selectedTag) {
|
||||
/** If the selected tag has been deleted, revert to All view. */
|
||||
@@ -89,11 +109,17 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
reloadTitles(tags: Array<SNTag | SNSmartTag>) {
|
||||
for(const tag of tags) {
|
||||
this.titles[tag.uuid] = tag.title;
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
onAppStateEvent(eventName, data) {
|
||||
onAppStateEvent(eventName: AppStateEvent, data?: any) {
|
||||
if (eventName === AppStateEvent.PreferencesChanged) {
|
||||
this.loadPreferences();
|
||||
} else if (eventName === AppStateEvent.TagChanged) {
|
||||
@@ -105,7 +131,7 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
|
||||
|
||||
/** @override */
|
||||
async onAppEvent(eventName) {
|
||||
async onAppEvent(eventName: ApplicationEvent) {
|
||||
super.onAppEvent(eventName);
|
||||
if (eventName === ApplicationEvent.LocalDataIncrementalLoad) {
|
||||
this.reloadNoteCounts();
|
||||
@@ -119,19 +145,25 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
reloadNoteCounts() {
|
||||
let allTags = [];
|
||||
let allTags: Array<SNTag | SNSmartTag> = [];
|
||||
if (this.state.tags) {
|
||||
allTags = allTags.concat(this.state.tags);
|
||||
}
|
||||
if (this.state.smartTags) {
|
||||
allTags = allTags.concat(this.state.smartTags);
|
||||
}
|
||||
const noteCounts = {};
|
||||
const noteCounts: NoteCounts = {};
|
||||
for (const tag of allTags) {
|
||||
const validNotes = SNNote.filterDummyNotes(tag.notes).filter((note) => {
|
||||
return !note.archived && !note.content.trashed;
|
||||
});
|
||||
noteCounts[tag.uuid] = validNotes.length;
|
||||
if (tag.isSmartTag()) {
|
||||
const notes = this.application.notesMatchingSmartTag(tag as SNSmartTag);
|
||||
noteCounts[tag.uuid] = notes.length;
|
||||
} else {
|
||||
const notes = this.application.referencesForItem(tag, ContentType.Note)
|
||||
.filter((note) => {
|
||||
return !note.archived && !note.trashed;
|
||||
})
|
||||
noteCounts[tag.uuid] = notes.length;
|
||||
}
|
||||
}
|
||||
this.setState({
|
||||
noteCounts: noteCounts
|
||||
@@ -144,17 +176,22 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
}
|
||||
const width = this.application.getPrefsService().getValue(PrefKeys.TagsPanelWidth);
|
||||
if (width) {
|
||||
this.panelPuppet.setWidth(width);
|
||||
if (this.panelPuppet.isCollapsed()) {
|
||||
this.panelPuppet.setWidth!(width);
|
||||
if (this.panelPuppet.isCollapsed!()) {
|
||||
this.application.getAppState().panelDidResize(
|
||||
PANEL_NAME_TAGS,
|
||||
this.panelPuppet.isCollapsed()
|
||||
this.panelPuppet.isCollapsed!()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPanelResize = (newWidth, lastLeft, isAtMaxWidth, isCollapsed) => {
|
||||
onPanelResize = (
|
||||
newWidth: number,
|
||||
lastLeft: number,
|
||||
isAtMaxWidth: boolean,
|
||||
isCollapsed: boolean
|
||||
) => {
|
||||
this.application.getPrefsService().setUserPrefValue(
|
||||
PrefKeys.TagsPanelWidth,
|
||||
newWidth,
|
||||
@@ -167,50 +204,49 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
registerComponentHandler() {
|
||||
this.unregisterComponent = this.application.componentManager.registerHandler({
|
||||
this.unregisterComponent = this.application.componentManager!.registerHandler({
|
||||
identifier: 'tags',
|
||||
areas: ['tags-list'],
|
||||
areas: [ComponentArea.TagsList],
|
||||
activationHandler: (component) => {
|
||||
this.component = component;
|
||||
},
|
||||
contextRequestHandler: (component) => {
|
||||
return null;
|
||||
contextRequestHandler: () => {
|
||||
return undefined;
|
||||
},
|
||||
actionHandler: (_, action, data) => {
|
||||
if (action === ComponentActions.SelectItem) {
|
||||
if (data.item.content_type === ContentTypes.Tag) {
|
||||
const tag = this.application.findItem({ uuid: data.item.uuid });
|
||||
if (action === ComponentAction.SelectItem) {
|
||||
if (data.item.content_type === ContentType.Tag) {
|
||||
const tag = this.application.findItem(data.item.uuid);
|
||||
if (tag) {
|
||||
this.selectTag(tag);
|
||||
this.selectTag(tag as SNTag);
|
||||
}
|
||||
} else if (data.item.content_type === ContentTypes.SmartTag) {
|
||||
this.application.createTemplateItem({
|
||||
contentType: ContentTypes.SmartTag,
|
||||
content: data.item.content
|
||||
}).then(smartTag => {
|
||||
this.selectTag(smartTag);
|
||||
} else if (data.item.content_type === ContentType.SmartTag) {
|
||||
this.application.createTemplateItem(
|
||||
ContentType.SmartTag,
|
||||
data.item.content
|
||||
).then(smartTag => {
|
||||
this.selectTag(smartTag as SNSmartTag);
|
||||
});
|
||||
}
|
||||
} else if (action === ComponentActions.ClearSelection) {
|
||||
} else if (action === ComponentAction.ClearSelection) {
|
||||
this.selectTag(this.state.smartTags[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async selectTag(tag) {
|
||||
async selectTag(tag: SNTag) {
|
||||
if (tag.isSmartTag()) {
|
||||
Object.defineProperty(tag, 'notes', {
|
||||
get: () => {
|
||||
return this.application.getNotesMatchingSmartTag({
|
||||
smartTag: tag
|
||||
});
|
||||
return this.application.notesMatchingSmartTag(tag as SNSmartTag);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (tag.content.conflict_of) {
|
||||
tag.content.conflict_of = null;
|
||||
this.application.saveItem({ item: tag });
|
||||
if (tag.conflictOf) {
|
||||
this.application.changeAndSaveItem(tag.uuid, (mutator) => {
|
||||
mutator.conflictOf = undefined;
|
||||
})
|
||||
}
|
||||
this.application.getAppState().setSelectedTag(tag);
|
||||
}
|
||||
@@ -219,9 +255,9 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
if (this.state.editingTag) {
|
||||
return;
|
||||
}
|
||||
const newTag = await this.application.createTemplateItem({
|
||||
contentType: ContentTypes.Tag
|
||||
});
|
||||
const newTag = await this.application.createTemplateItem(
|
||||
ContentType.Tag
|
||||
);
|
||||
this.setState({
|
||||
tags: [newTag].concat(this.state.tags),
|
||||
previousTag: this.state.selectedTag,
|
||||
@@ -231,14 +267,14 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
});
|
||||
}
|
||||
|
||||
tagTitleDidChange(tag) {
|
||||
onTagTitleChange(tag: SNTag | SNSmartTag) {
|
||||
this.setState({
|
||||
editingTag: tag
|
||||
});
|
||||
}
|
||||
|
||||
async saveTag($event, tag) {
|
||||
$event.target.blur();
|
||||
async saveTag($event: Event, tag: SNTag) {
|
||||
($event.target! as HTMLInputElement).blur();
|
||||
await this.setState({
|
||||
editingTag: null,
|
||||
});
|
||||
@@ -246,8 +282,8 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
if (!tag.title || tag.title.length === 0) {
|
||||
let newSelectedTag = this.state.selectedTag;
|
||||
if (this.state.editingTag) {
|
||||
tag.title = this.editingOriginalName;
|
||||
this.editingOriginalName = null;
|
||||
this.titles[tag.uuid] = this.editingOriginalName;
|
||||
this.editingOriginalName = undefined;
|
||||
} else if (this.state.newTag) {
|
||||
newSelectedTag = this.state.previousTag;
|
||||
}
|
||||
@@ -259,14 +295,14 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
return;
|
||||
}
|
||||
|
||||
this.editingOriginalName = null;
|
||||
this.editingOriginalName = undefined;
|
||||
|
||||
const matchingTag = this.application.findTag({ title: tag.title });
|
||||
const matchingTag = this.application.findTag(tag.title);
|
||||
const alreadyExists = matchingTag && matchingTag !== tag;
|
||||
if (this.state.newTag === tag && alreadyExists) {
|
||||
this.application.alertService.alert({
|
||||
text: "A tag with this name already exists."
|
||||
});
|
||||
this.application.alertService!.alert(
|
||||
"A tag with this name already exists."
|
||||
);
|
||||
this.setState({
|
||||
newTag: null,
|
||||
tags: this.getMappedTags(),
|
||||
@@ -274,40 +310,48 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.application.saveItem({ item: tag });
|
||||
this.application.changeAndSaveItem(tag.uuid, (mutator) => {
|
||||
const tagMutator = mutator as TagMutator;
|
||||
tagMutator.title = this.titles[tag.uuid]!;
|
||||
});
|
||||
this.selectTag(tag);
|
||||
this.setState({
|
||||
newTag: null
|
||||
});
|
||||
}
|
||||
|
||||
async selectedRenameTag($event, tag) {
|
||||
async selectedRenameTag(tag: SNTag) {
|
||||
this.editingOriginalName = tag.title;
|
||||
await this.setState({
|
||||
editingTag: tag
|
||||
});
|
||||
document.getElementById('tag-' + tag.uuid).focus();
|
||||
document.getElementById('tag-' + tag.uuid)!.focus();
|
||||
}
|
||||
|
||||
selectedDeleteTag(tag) {
|
||||
selectedDeleteTag(tag: SNTag) {
|
||||
this.removeTag(tag);
|
||||
}
|
||||
|
||||
removeTag(tag) {
|
||||
this.application.alertService.confirm({
|
||||
text: STRING_DELETE_TAG,
|
||||
destructive: true,
|
||||
onConfirm: () => {
|
||||
this.application.deleteItem({ item: tag });
|
||||
removeTag(tag: SNTag) {
|
||||
this.application.alertService!.confirm(
|
||||
STRING_DELETE_TAG,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
() => {
|
||||
/* On confirm */
|
||||
this.application.deleteItem(tag);
|
||||
this.selectTag(this.state.smartTags[0]);
|
||||
}
|
||||
});
|
||||
},
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class TagsPanel {
|
||||
export class TagsPanel extends WebDirective {
|
||||
constructor() {
|
||||
super();
|
||||
this.restrict = 'E';
|
||||
this.scope = {
|
||||
application: '='
|
||||
Reference in New Issue
Block a user