feat: add empty trash option

This commit is contained in:
Antonella Sgarlatta
2021-05-12 18:51:28 -03:00
parent 075f1b910b
commit 674f14bf89
5 changed files with 65 additions and 23 deletions

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.5001 13.3333H15.8334V14.9999H12.5001V13.3333ZM12.5001 6.66658H18.3334V8.33325H12.5001V6.66658ZM12.5001 9.99992H17.5001V11.6666H12.5001V9.99992ZM9.16675 8.33325V14.9999H4.16675V8.33325H9.16675ZM10.8334 6.66658H2.50008V14.9999C2.50008 15.4419 2.67568 15.8659 2.98824 16.1784C3.3008 16.491 3.72472 16.6666 4.16675 16.6666H9.16675C9.60878 16.6666 10.0327 16.491 10.3453 16.1784C10.6578 15.8659 10.8334 15.4419 10.8334 14.9999V6.66658ZM11.6667 4.16659H9.16675L8.33342 3.33325H5.00008L4.16675 4.16659H1.66675V5.83325H11.6667V4.16659Z" />
</svg>

After

Width:  |  Height:  |  Size: 648 B

View File

@@ -10,6 +10,7 @@ import ChevronRightIcon from '../../icons/ic-chevron-right.svg';
import RestoreIcon from '../../icons/ic-restore.svg'; import RestoreIcon from '../../icons/ic-restore.svg';
import CloseIcon from '../../icons/ic-close.svg'; import CloseIcon from '../../icons/ic-close.svg';
import PasswordIcon from '../../icons/ic-textbox-password.svg'; import PasswordIcon from '../../icons/ic-textbox-password.svg';
import TrashSweepIcon from '../../icons/ic-trash-sweep.svg';
import { toDirective } from './utils'; import { toDirective } from './utils';
const ICONS = { const ICONS = {
@@ -25,6 +26,7 @@ const ICONS = {
'restore': RestoreIcon, 'restore': RestoreIcon,
'close': CloseIcon, 'close': CloseIcon,
'password': PasswordIcon, 'password': PasswordIcon,
'trash-sweep': TrashSweepIcon,
}; };
type Props = { type Props = {

View File

@@ -238,29 +238,44 @@ export const NotesOptions = observer(
</button> </button>
)} )}
{trashed && ( {trashed && (
<button <>
onBlur={closeOnBlur} <button
className={`${buttonClass} py-1.5`} onBlur={closeOnBlur}
onClick={async () => { className={`${buttonClass} py-1.5`}
await appState.notes.setTrashSelectedNotes(false); onClick={async () => {
}} await appState.notes.setTrashSelectedNotes(false);
> }}
<Icon type='restore' className={iconClass} /> >
Restore <Icon type='restore' className={iconClass} />
</button> Restore
</button>
<button
onBlur={closeOnBlur}
className={`${buttonClass} py-1.5`}
onClick={async () => {
await appState.notes.deleteNotesPermanently();
}}
>
<Icon type="close" className="fill-current color-danger mr-2" />
<span className="color-danger">Delete permanently</span>
</button>
<button
onBlur={closeOnBlur}
className={`${buttonClass} py-1.5`}
onClick={async () => {
await appState.notes.emptyTrash();
}}
>
<div className="flex items-start">
<Icon type="trash-sweep" className="fill-current color-danger mr-2" />
<div className="flex-row">
<div className="color-danger">Empty Trash</div>
<div className="text-xs">{appState.notes.trashedNotesCount} notes in Trash</div>
</div>
</div>
</button>
</>
)} )}
{appState.selectedTag?.isTrashTag && (
<button
onBlur={closeOnBlur}
className={`${buttonClass} py-1.5`}
onClick={async () => {
await appState.notes.deleteNotesPermanently();
}}
>
<Icon type="close" className="fill-current color-danger mr-2" />
<span className="color-danger">Delete permanently</span>
</button>
)}
</> </>
); );
} }

View File

@@ -1,6 +1,6 @@
import { confirmDialog } from '@/services/alertService'; import { confirmDialog } from '@/services/alertService';
import { KeyboardModifier } from '@/services/ioService'; import { KeyboardModifier } from '@/services/ioService';
import { Strings, StringUtils } from '@/strings'; import { StringEmptyTrash, Strings, StringUtils } from '@/strings';
import { import {
UuidString, UuidString,
SNNote, SNNote,
@@ -40,6 +40,7 @@ export class NotesState {
showProtectedWarning: observable, showProtectedWarning: observable,
selectedNotesCount: computed, selectedNotesCount: computed,
trashedNotesCount: computed,
deleteNotesPermanently: action, deleteNotesPermanently: action,
selectNote: action, selectNote: action,
@@ -55,6 +56,7 @@ export class NotesState {
removeTagFromSelectedNotes: action, removeTagFromSelectedNotes: action,
isTagInSelectedNotes: action, isTagInSelectedNotes: action,
setShowProtectedWarning: action, setShowProtectedWarning: action,
emptyTrash: action,
}); });
appEventListeners.push( appEventListeners.push(
@@ -78,6 +80,10 @@ export class NotesState {
return Object.keys(this.selectedNotes).length; return Object.keys(this.selectedNotes).length;
} }
get trashedNotesCount(): number {
return this.application.getTrashedItems().length;
}
async runProtectedAction(action: (note: SNNote) => void, notes: SNNote[]): Promise<void> { async runProtectedAction(action: (note: SNNote) => void, notes: SNNote[]): Promise<void> {
let protectedNotesAccessRequest: Promise<boolean>; let protectedNotesAccessRequest: Promise<boolean>;
await Promise.all( await Promise.all(
@@ -358,6 +364,18 @@ export class NotesState {
this.showProtectedWarning = show; this.showProtectedWarning = show;
} }
async emptyTrash() {
if (
await confirmDialog({
text: StringEmptyTrash(this.trashedNotesCount),
confirmButtonStyle: 'danger',
})
) {
this.application.emptyTrash();
this.application.sync();
}
}
private get io() { private get io() {
return this.application.io; return this.application.io;
} }

View File

@@ -139,6 +139,10 @@
overflow-y: scroll; overflow-y: scroll;
} }
.items-start {
align-items: flex-start;
}
/** /**
* A button that is just an icon. Separated from .sn-button because there * A button that is just an icon. Separated from .sn-button because there
* is almost no style overlap. * is almost no style overlap.