From 156b47b1b5f6d6e0b39e8427245852dd8611987a Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Tue, 8 Feb 2022 21:14:15 -0800 Subject: [PATCH] fix: Auth persistence to `localStorage` (#3078) * fix: user, team, and policies should be persisted to localStorage for faster boot * capture instead of ignore errors --- app/models/BaseModel.ts | 35 +++++++++++++++++++++++++++++------ app/stores/AuthStore.ts | 10 ++++++---- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/app/models/BaseModel.ts b/app/models/BaseModel.ts index 73fc11107..108243fe8 100644 --- a/app/models/BaseModel.ts +++ b/app/models/BaseModel.ts @@ -22,7 +22,7 @@ export default class BaseModel { try { // ensure that the id is passed if the document has one if (!params) { - params = this.toJS(); + params = this.toAPI(); } const model = await this.store.save({ ...params, id: this.id }); @@ -30,7 +30,7 @@ export default class BaseModel { // if saving is successful set the new values on the model itself set(this, { ...params, ...model }); - this.persistedAttributes = this.toJS(); + this.persistedAttributes = this.toAPI(); return model; } finally { @@ -40,7 +40,7 @@ export default class BaseModel { updateFromJson = (data: any) => { set(this, data); - this.persistedAttributes = this.toJS(); + this.persistedAttributes = this.toAPI(); }; fetch = (options?: any) => { @@ -64,15 +64,38 @@ export default class BaseModel { }; /** - * Returns a plain object representation of the model + * Returns a plain object representation of fields on the model for + * persistence to the server API * * @returns {Record} */ - toJS = (): Record => { + toAPI = (): Record => { const fields = getFieldsForModel(this); return pick(this, fields) || []; }; + /** + * Returns a plain object representation of all the properties on the model + * overrides the inbuilt toJSON method to avoid attempting to serialize store + * + * @returns {Record} + */ + toJSON() { + const output: Partial = {}; + + for (const property in this) { + if ( + // eslint-disable-next-line no-prototype-builtins + this.hasOwnProperty(property) && + !["persistedAttributes", "store", "isSaving"].includes(property) + ) { + output[property] = this[property]; + } + } + + return output; + } + /** * Returns a boolean indicating if the model has changed since it was last * persisted to the server @@ -80,7 +103,7 @@ export default class BaseModel { * @returns boolean true if unsaved */ isDirty(): boolean { - const attributes = this.toJS(); + const attributes = this.toAPI(); if (Object.keys(attributes).length === 0) { console.warn("Checking dirty on model with no @Field decorators"); diff --git a/app/stores/AuthStore.ts b/app/stores/AuthStore.ts index 2fc696aa1..226182eb3 100644 --- a/app/stores/AuthStore.ts +++ b/app/stores/AuthStore.ts @@ -68,19 +68,21 @@ export default class AuthStore { try { data = JSON.parse(localStorage.getItem(AUTH_STORE) || "{}"); - } catch (_) { - // no-op Safari private mode + } catch (err) { + Sentry.captureException(err); } this.rehydrate(data); + // persists this entire store to localstorage whenever any keys are changed autorun(() => { try { localStorage.setItem(AUTH_STORE, this.asJson); - } catch (_) { - // no-op Safari private mode + } catch (err) { + Sentry.captureException(err); } }); + // listen to the localstorage value changing in other tabs to react to // signin/signout events in other tabs and follow suite. window.addEventListener("storage", (event) => {