From 3974420fd012fa7f6fe3ebbf50a26d9f1008aeb5 Mon Sep 17 00:00:00 2001 From: Joe Ipson Date: Tue, 23 Nov 2021 18:06:24 -0700 Subject: [PATCH 01/17] Added 'Goto Today functionality (#22), Added Auto-Save functionality (#23), Added sorting on search functionality (#25). --- Dockerfile | 1 - app/models.py | 1 + app/routes.py | 32 ++++++++++++++++-- client/src/components/Header.vue | 19 ++++++----- client/src/services/sidebar.ts | 9 +++++ client/src/views/Day.vue | 9 +++++ client/src/views/Home.vue | 26 +++++++++++---- client/src/views/Note.vue | 9 +++++ ...f2_added_auto_save_column_to_user_table.py | 33 +++++++++++++++++++ requirements.txt | 3 +- 10 files changed, 123 insertions(+), 19 deletions(-) create mode 100644 migrations/versions/ad68860179f2_added_auto_save_column_to_user_table.py diff --git a/Dockerfile b/Dockerfile index a6776c5..ab9ce2c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,6 @@ RUN apk add build-base libffi-dev RUN \ cd /app && \ - pip install gunicorn && \ pip install -r requirements.txt && \ chmod +x run.sh && \ chmod +x verify_env.py && \ diff --git a/app/models.py b/app/models.py index fb22b01..e373b47 100644 --- a/app/models.py +++ b/app/models.py @@ -51,6 +51,7 @@ class User(db.Model): uuid = db.Column(GUID, primary_key=True, index=True, unique=True, default=lambda: uuid.uuid4()) username = db.Column(db.String(64), unique=True, nullable=False) password_hash = db.Column(db.String(128), nullable=False) + auto_save = db.Column(db.Boolean, nullable=True) notes = db.relationship('Note', lazy='dynamic', cascade='all, delete, delete-orphan') meta = db.relationship('Meta', lazy='dynamic', cascade='all, delete, delete-orphan') diff --git a/app/routes.py b/app/routes.py index 50c426c..04c8d28 100644 --- a/app/routes.py +++ b/app/routes.py @@ -306,8 +306,34 @@ def sidebar_data(): tags = sorted(set([a.name for a in user.meta.filter_by(kind="tag").all()]), key=lambda s: s.lower()) projects = sorted(set([a.name for a in user.meta.filter_by(kind="project").all()]), key=lambda s: s.lower()) tasks = sorted([a.serialize for a in user.meta.filter_by(kind="task").all()], key=lambda task: task['note_id']) + auto_save = user.auto_save - return jsonify(tags=tags,projects=projects,notes=notes,tasks=tasks), 200 + return jsonify(tags=tags,projects=projects,notes=notes,tasks=tasks,auto_save=auto_save), 200 + + +@app.route('/api/toggle_auto_save', methods=['POST']) +@jwt_required() +def toggle_auto_save(): + req = request.get_json() + auto_save = req.get('auto_save', False) + + username = get_jwt_identity() + + if not username: + abort(401) + + user = User.query.filter_by(username=username.lower()).first() + + if not user: + abort(400) + + user.auto_save = auto_save + + db.session.add(user) + db.session.flush() + db.session.commit() + + return jsonify({}), 200 @app.route('/api/search', methods=['POST']) @@ -365,7 +391,9 @@ def search(): cleaned_note['projects'] = sorted(set([x.name for x in note.meta.filter_by(kind="project").all()]), key=lambda s: s.lower()) notes.append(cleaned_note) - return jsonify(notes=notes), 200 + sorted_nodes = sorted(notes, key=lambda s: s['title'].lower()) + + return jsonify(notes=sorted_nodes), 200 @app.route('/', defaults={'path': ''}) diff --git a/client/src/components/Header.vue b/client/src/components/Header.vue index 848308d..97061cb 100644 --- a/client/src/components/Header.vue +++ b/client/src/components/Header.vue @@ -76,14 +76,16 @@
- - - - - Settings (coming soon) + + + + + {{ sidebar.autoSave ? 'Disable Auto-Save' : 'Enable Auto-Save' }} + + Logout
@@ -122,6 +124,7 @@ export default class Header extends Vue { public sidebar = SidebarInst; public options!: IHeaderOptions; public isSaving: boolean = false; + public isSwitchedCustom: boolean = false; public toggleSidebar(show = false) { this.sidebar.hide = show; diff --git a/client/src/services/sidebar.ts b/client/src/services/sidebar.ts index ea2539b..f9962ae 100644 --- a/client/src/services/sidebar.ts +++ b/client/src/services/sidebar.ts @@ -17,6 +17,7 @@ class SidebarSerivce { public projects: string[] = []; public notes: INote[] = []; public calLoading: boolean = false; + public autoSave: boolean = false; public date: any = null; public sidebarLoading: boolean = false; public searchLoading: boolean = false; @@ -99,6 +100,7 @@ class SidebarSerivce { this.tasks = res.data.tasks; this.projects = res.data.projects; this.notes = res.data.notes; + this.autoSave = res.data.auto_save; } if (this.selectedSearch.length && this.searchString.length) { @@ -138,6 +140,13 @@ class SidebarSerivce { this.getSidebarInfo(); } catch (e) {} } + + public async toggleAutoSave(autoSave: boolean) { + try { + await Requests.post('/toggle_auto_save', {auto_save: autoSave}); + this.getSidebarInfo(); + } catch (e) {} + } } // Make it a singleton diff --git a/client/src/views/Day.vue b/client/src/views/Day.vue index 7039b4c..1a37939 100644 --- a/client/src/views/Day.vue +++ b/client/src/views/Day.vue @@ -231,12 +231,21 @@ export default class Day extends Vue { this.unsavedChanges = true; this.title = `* ${this.headerOptions.title}`; this.headerOptions.saveDisabled = false; + + if (this.sidebar.autoSave) { + this.autoSaveThrottle(); + } } else { this.title = this.headerOptions.title; this.headerOptions.saveDisabled = true; } } + public autoSaveThrottle = _.debounce(() => this.saveDay(), 3000, { + leading: false, + trailing: true + }); + unsavedAlert(e: Event) { if (this.unsavedChanges) { // Attempt to modify event will trigger Chrome/Firefox alert msg diff --git a/client/src/views/Home.vue b/client/src/views/Home.vue index 720ef7d..0c50a31 100644 --- a/client/src/views/Home.vue +++ b/client/src/views/Home.vue @@ -1,14 +1,22 @@