feat: add protect option to menu
This commit is contained in:
3
app/assets/icons/ic-textbox-password.svg
Normal file
3
app/assets/icons/ic-textbox-password.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.2 7.8H20.4V16.2H16.2V17.88C16.2 18.1028 16.2885 18.3164 16.446 18.474C16.6035 18.6315 16.8172 18.72 17.04 18.72H18.72V20.4H16.62C16.158 20.4 15.36 20.022 15.36 19.56C15.36 20.022 14.562 20.4 14.1 20.4H12V18.72H13.68C13.9028 18.72 14.1164 18.6315 14.2739 18.474C14.4315 18.3164 14.52 18.1028 14.52 17.88V6.12C14.52 5.89722 14.4315 5.68356 14.2739 5.52603C14.1164 5.3685 13.9028 5.28 13.68 5.28H12V3.6H14.1C14.562 3.6 15.36 3.978 15.36 4.44C15.36 3.978 16.158 3.6 16.62 3.6H18.72V5.28H17.04C16.8172 5.28 16.6035 5.3685 16.446 5.52603C16.2885 5.68356 16.2 5.89722 16.2 6.12V7.8ZM3.59998 7.8H12.84V9.48H5.27998V14.52H12.84V16.2H3.59998V7.8ZM18.72 14.52V9.48H16.2V14.52H18.72ZM9.05998 12C9.05998 11.6658 8.92723 11.3453 8.69093 11.109C8.45463 10.8727 8.13415 10.74 7.79998 10.74C7.4658 10.74 7.14532 10.8727 6.90902 11.109C6.67273 11.3453 6.53998 11.6658 6.53998 12C6.53998 12.3342 6.67273 12.6547 6.90902 12.891C7.14532 13.1272 7.4658 13.26 7.79998 13.26C8.13415 13.26 8.45463 13.1272 8.69093 12.891C8.92723 12.6547 9.05998 12.3342 9.05998 12ZM12.84 11.0676C12.3276 10.5972 11.5296 10.6392 11.0592 11.16C10.5888 11.664 10.6308 12.462 11.16 12.9324C11.622 13.3692 12.3612 13.3692 12.84 12.9324V11.0676Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -9,6 +9,7 @@ import HashtagIcon from '../../icons/ic-hashtag.svg';
|
||||
import ChevronRightIcon from '../../icons/ic-chevron-right.svg';
|
||||
import RestoreIcon from '../../icons/ic-restore.svg';
|
||||
import CloseIcon from '../../icons/ic-close.svg';
|
||||
import PasswordIcon from '../../icons/ic-textbox-password.svg';
|
||||
import { toDirective } from './utils';
|
||||
|
||||
const ICONS = {
|
||||
@@ -23,6 +24,7 @@ const ICONS = {
|
||||
'chevron-right': ChevronRightIcon,
|
||||
'restore': RestoreIcon,
|
||||
'close': CloseIcon,
|
||||
'password': PasswordIcon,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -35,6 +35,7 @@ export const NotesOptions = observer(
|
||||
const notes = Object.values(appState.notes.selectedNotes);
|
||||
const hidePreviews = toggleOn(note => note.hidePreview);
|
||||
const locked = toggleOn(note => note.locked);
|
||||
const protect = toggleOn(note => note.protected);
|
||||
const archived = notes.some((note) => note.archived);
|
||||
const unarchived = notes.some((note) => !note.archived);
|
||||
const trashed = notes.some((note) => note.trashed);
|
||||
@@ -84,6 +85,19 @@ export const NotesOptions = observer(
|
||||
Show preview
|
||||
</span>
|
||||
</Switch>
|
||||
<Switch
|
||||
onBlur={closeOnBlur}
|
||||
className="px-3 py-1.5"
|
||||
checked={protect}
|
||||
onChange={() => {
|
||||
appState.notes.setProtectSelectedNotes(!protect);
|
||||
}}
|
||||
>
|
||||
<span className="flex items-center">
|
||||
<Icon type="password" className={iconClass} />
|
||||
Protect
|
||||
</span>
|
||||
</Switch>
|
||||
<div className="h-1px my-2 bg-border"></div>
|
||||
{appState.tags.tagsCount > 0 && (
|
||||
<Disclosure
|
||||
|
||||
@@ -26,6 +26,7 @@ export class NotesState {
|
||||
top: 0,
|
||||
left: 0,
|
||||
};
|
||||
showProtectedWarning = false;
|
||||
|
||||
constructor(
|
||||
private application: WebApplication,
|
||||
@@ -36,6 +37,7 @@ export class NotesState {
|
||||
selectedNotes: observable,
|
||||
contextMenuOpen: observable,
|
||||
contextMenuPosition: observable,
|
||||
showProtectedWarning: observable,
|
||||
|
||||
selectedNotesCount: computed,
|
||||
|
||||
@@ -52,6 +54,7 @@ export class NotesState {
|
||||
addTagToSelectedNotes: action,
|
||||
removeTagFromSelectedNotes: action,
|
||||
isTagInSelectedNotes: action,
|
||||
setShowProtectedWarning: action,
|
||||
});
|
||||
|
||||
appEventListeners.push(
|
||||
@@ -75,6 +78,23 @@ export class NotesState {
|
||||
return Object.keys(this.selectedNotes).length;
|
||||
}
|
||||
|
||||
async runProtectedAction(action: (note: SNNote) => void, notes: SNNote[]): Promise<void> {
|
||||
let protectedNotesAccessRequest: Promise<boolean>;
|
||||
await Promise.all(
|
||||
notes.map(async (note) => {
|
||||
if (note.protected) {
|
||||
if (!protectedNotesAccessRequest) {
|
||||
protectedNotesAccessRequest =
|
||||
this.application.authorizeNoteAccess(note);
|
||||
}
|
||||
}
|
||||
if (!note.protected || await protectedNotesAccessRequest) {
|
||||
action(note);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async selectNotesRange(selectedNote: SNNote): Promise<void> {
|
||||
const notes = this.application.getDisplayableItems(
|
||||
ContentType.Note
|
||||
@@ -85,32 +105,18 @@ export class NotesState {
|
||||
const selectedNoteIndex = notes.findIndex(
|
||||
(note) => note.uuid == selectedNote.uuid
|
||||
);
|
||||
let protectedNotesAccessRequest: Promise<boolean>;
|
||||
let notesToSelect = [];
|
||||
|
||||
let notesToSelect = [];
|
||||
if (selectedNoteIndex > lastSelectedNoteIndex) {
|
||||
notesToSelect = notes.slice(lastSelectedNoteIndex, selectedNoteIndex + 1);
|
||||
} else {
|
||||
notesToSelect = notes.slice(selectedNoteIndex, lastSelectedNoteIndex + 1);
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
notesToSelect.map(async (note) => {
|
||||
const requestAccess =
|
||||
note.protected && this.application.hasProtectionSources();
|
||||
if (requestAccess) {
|
||||
if (!protectedNotesAccessRequest) {
|
||||
protectedNotesAccessRequest =
|
||||
this.application.authorizeNoteAccess(note);
|
||||
}
|
||||
}
|
||||
if (!requestAccess || (await protectedNotesAccessRequest)) {
|
||||
this.selectedNotes[note.uuid] = note;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.lastSelectedNote = selectedNote;
|
||||
this.runProtectedAction((note) => {
|
||||
this.selectedNotes[note.uuid] = note;
|
||||
this.lastSelectedNote = selectedNote;
|
||||
}, notesToSelect);
|
||||
}
|
||||
|
||||
async selectNote(note: SNNote): Promise<void> {
|
||||
@@ -211,7 +217,7 @@ export class NotesState {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.changeSelectedNotes((mutator) => {
|
||||
await this.changeSelectedNotes((mutator) => {
|
||||
mutator.trashed = trashed;
|
||||
});
|
||||
this.unselectNotes();
|
||||
@@ -258,7 +264,7 @@ export class NotesState {
|
||||
await this.application.deleteItem(note);
|
||||
}
|
||||
} else {
|
||||
this.changeSelectedNotes((mutator) => {
|
||||
await this.changeSelectedNotes((mutator) => {
|
||||
mutator.trashed = true;
|
||||
});
|
||||
}
|
||||
@@ -282,7 +288,7 @@ export class NotesState {
|
||||
return;
|
||||
}
|
||||
|
||||
this.changeSelectedNotes((mutator) => {
|
||||
await this.changeSelectedNotes((mutator) => {
|
||||
mutator.archived = archived;
|
||||
});
|
||||
|
||||
@@ -292,6 +298,25 @@ export class NotesState {
|
||||
});
|
||||
}
|
||||
|
||||
async setProtectSelectedNotes(protect: boolean): Promise<void> {
|
||||
if (protect) {
|
||||
await this.changeSelectedNotes((mutator) => {
|
||||
mutator.protected = protect;
|
||||
});
|
||||
if (!this.application.hasProtectionSources()) {
|
||||
this.setShowProtectedWarning(true);
|
||||
}
|
||||
} else {
|
||||
const selectedNotes = Object.values(this.selectedNotes);
|
||||
this.runProtectedAction(async (note) => {
|
||||
await this.application.changeItem(note.uuid, (mutator) => {
|
||||
mutator.protected = protect;
|
||||
});
|
||||
}, selectedNotes);
|
||||
this.setShowProtectedWarning(false);
|
||||
}
|
||||
}
|
||||
|
||||
unselectNotes(): void {
|
||||
this.selectedNotes = {};
|
||||
}
|
||||
@@ -326,6 +351,10 @@ export class NotesState {
|
||||
);
|
||||
}
|
||||
|
||||
setShowProtectedWarning(show: boolean): void {
|
||||
this.showProtectedWarning = show;
|
||||
}
|
||||
|
||||
private get io() {
|
||||
return this.application.io;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#editor-column.section.editor.sn-component(aria-label='Note')
|
||||
protected-note-panel.h-full.flex.justify-center.items-center(
|
||||
ng-if='self.state.showProtectedWarning'
|
||||
ng-if='self.appState.notes.showProtectedWarning'
|
||||
app-state='self.appState'
|
||||
on-view-note='self.dismissProtectedWarning()'
|
||||
)
|
||||
.flex-grow.flex.flex-col(
|
||||
ng-if='!self.state.showProtectedWarning'
|
||||
ng-if='!self.appState.notes.showProtectedWarning'
|
||||
)
|
||||
.sn-component
|
||||
.sk-app-bar.no-edges(
|
||||
|
||||
@@ -85,7 +85,6 @@ type EditorState = {
|
||||
textareaUnloading: boolean;
|
||||
/** Fields that can be directly mutated by the template */
|
||||
mutable: any;
|
||||
showProtectedWarning: boolean;
|
||||
};
|
||||
|
||||
type EditorValues = {
|
||||
@@ -241,7 +240,6 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
mutable: {
|
||||
tagsString: '',
|
||||
},
|
||||
showProtectedWarning: false,
|
||||
} as EditorState;
|
||||
}
|
||||
|
||||
@@ -291,8 +289,8 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
async handleEditorNoteChange() {
|
||||
this.cancelPendingSetStatus();
|
||||
const note = this.editor.note;
|
||||
const showProtectedWarning =
|
||||
note.protected && !this.application.hasProtectionSources();
|
||||
const showProtectedWarning = note.protected && !this.application.hasProtectionSources();
|
||||
this.setShowProtectedWarning(showProtectedWarning);
|
||||
await this.setState({
|
||||
showActionsMenu: false,
|
||||
showOptionsMenu: false,
|
||||
@@ -300,7 +298,6 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
showHistoryMenu: false,
|
||||
altKeyDown: false,
|
||||
noteStatus: undefined,
|
||||
showProtectedWarning,
|
||||
});
|
||||
this.editorValues.title = note.title;
|
||||
this.editorValues.text = note.text;
|
||||
@@ -318,9 +315,7 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
}
|
||||
|
||||
async dismissProtectedWarning() {
|
||||
await this.setState({
|
||||
showProtectedWarning: false,
|
||||
});
|
||||
this.setShowProtectedWarning(false);
|
||||
this.focusTitle();
|
||||
}
|
||||
|
||||
@@ -658,6 +653,10 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
}
|
||||
}
|
||||
|
||||
setShowProtectedWarning(show: boolean) {
|
||||
this.application.getAppState().notes.setShowProtectedWarning(show);
|
||||
}
|
||||
|
||||
async deleteNote(permanently: boolean) {
|
||||
if (this.editor.isTemplateNote) {
|
||||
this.application.alertService!.alert(STRING_DELETE_PLACEHOLDER_ATTEMPT);
|
||||
@@ -767,9 +766,7 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
} else {
|
||||
const note = await this.application.protectNote(this.note);
|
||||
if (note?.protected && !this.application.hasProtectionSources()) {
|
||||
this.setState({
|
||||
showProtectedWarning: true,
|
||||
});
|
||||
this.setShowProtectedWarning(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user