mirror of
https://gitlab.com/walljm/dynamicbible.git
synced 2025-07-23 23:39:50 -04:00
fix issues with initial state being stored then retrieved overwriting state from previous session.
fix issue with getState() not returning the initial state. fix issue with excluded search terms not being ignored.
This commit is contained in:
parent
d93400b911
commit
ff33cdaf34
@ -44,7 +44,7 @@ class StateService<TState, TAction extends { type: string }> {
|
|||||||
* derived service class.
|
* derived service class.
|
||||||
*/
|
*/
|
||||||
protected getState(): TState {
|
protected getState(): TState {
|
||||||
return this.store.getState();
|
return this.internalState$.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
export interface IStorable<T> {
|
export interface IStorable<T> {
|
||||||
readonly createdOn: string;
|
readonly createdOn: string;
|
||||||
|
readonly type: StorableType;
|
||||||
readonly value: T;
|
readonly value: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Storable<T> implements IStorable<T> {
|
export class Storable<T> implements IStorable<T> {
|
||||||
constructor(v: T) {
|
constructor(v: T, type: StorableType = StorableType.modified) {
|
||||||
this.value = v;
|
this.value = v;
|
||||||
|
this.type = type;
|
||||||
this.createdOn = new Date().toISOString();
|
this.createdOn = new Date().toISOString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type: StorableType;
|
||||||
createdOn: string;
|
createdOn: string;
|
||||||
value: T;
|
value: T;
|
||||||
}
|
}
|
||||||
@ -16,3 +19,8 @@ export class Storable<T> implements IStorable<T> {
|
|||||||
export interface UserVersion {
|
export interface UserVersion {
|
||||||
version: number;
|
version: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum StorableType {
|
||||||
|
initial,
|
||||||
|
modified
|
||||||
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { AppState } from '../models/app-state';
|
import { AppState } from '../models/app-state';
|
||||||
import { Overlap } from '../common/bible-reference';
|
import { Overlap } from '../common/bible-reference';
|
||||||
|
import { StorableType } from '../common/storable';
|
||||||
|
|
||||||
export const initialState: AppState = {
|
export const initialState: AppState = {
|
||||||
user: null,
|
user: null,
|
||||||
currentCards: {
|
currentCards: {
|
||||||
|
type: StorableType.initial,
|
||||||
createdOn: new Date(0).toISOString(),
|
createdOn: new Date(0).toISOString(),
|
||||||
value: [],
|
value: [],
|
||||||
},
|
},
|
||||||
@ -12,12 +14,14 @@ export const initialState: AppState = {
|
|||||||
currentSavedPage: null,
|
currentSavedPage: null,
|
||||||
savedPages: null,
|
savedPages: null,
|
||||||
notes: {
|
notes: {
|
||||||
|
type: StorableType.initial,
|
||||||
createdOn: new Date(0).toISOString(),
|
createdOn: new Date(0).toISOString(),
|
||||||
value: [],
|
value: [],
|
||||||
},
|
},
|
||||||
savedPagesLoaded: false,
|
savedPagesLoaded: false,
|
||||||
error: null,
|
error: null,
|
||||||
settings: {
|
settings: {
|
||||||
|
type: StorableType.initial,
|
||||||
createdOn: new Date(0).toISOString(),
|
createdOn: new Date(0).toISOString(),
|
||||||
value: {
|
value: {
|
||||||
displaySettings: {
|
displaySettings: {
|
||||||
|
@ -40,7 +40,73 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
private paragraphs: HashTable<Paragraph>;
|
private paragraphs: HashTable<Paragraph>;
|
||||||
private searchIndexArray: string[];
|
private searchIndexArray: string[];
|
||||||
private autocomplete: string[];
|
private autocomplete: string[];
|
||||||
|
private excludedWords: Map<string, number> = new Map([
|
||||||
|
['us', 0],
|
||||||
|
['these', 0],
|
||||||
|
['her', 0],
|
||||||
|
['saith', 0],
|
||||||
|
['shalt', 0],
|
||||||
|
['let', 0],
|
||||||
|
['do', 0],
|
||||||
|
['your', 0],
|
||||||
|
['we', 0],
|
||||||
|
['no', 0],
|
||||||
|
['go', 0],
|
||||||
|
['if', 0],
|
||||||
|
['at', 0],
|
||||||
|
['an', 0],
|
||||||
|
['so', 0],
|
||||||
|
['before', 0],
|
||||||
|
['also', 0],
|
||||||
|
['on', 0],
|
||||||
|
['had', 0],
|
||||||
|
['you', 0],
|
||||||
|
['there', 0],
|
||||||
|
['then', 0],
|
||||||
|
['up', 0],
|
||||||
|
['by', 0],
|
||||||
|
['upon', 0],
|
||||||
|
['were', 0],
|
||||||
|
['are', 0],
|
||||||
|
['this', 0],
|
||||||
|
['when', 0],
|
||||||
|
['thee', 0],
|
||||||
|
['their', 0],
|
||||||
|
['ye', 0],
|
||||||
|
['will', 0],
|
||||||
|
['as', 0],
|
||||||
|
['thy', 0],
|
||||||
|
['my', 0],
|
||||||
|
['me', 0],
|
||||||
|
['have', 0],
|
||||||
|
['from', 0],
|
||||||
|
['was', 0],
|
||||||
|
['but', 0],
|
||||||
|
['which', 0],
|
||||||
|
['thou', 0],
|
||||||
|
['all', 0],
|
||||||
|
['it', 0],
|
||||||
|
['with', 0],
|
||||||
|
['them', 0],
|
||||||
|
['him', 0],
|
||||||
|
['they', 0],
|
||||||
|
['is', 0],
|
||||||
|
['be', 0],
|
||||||
|
['not', 0],
|
||||||
|
['his', 0],
|
||||||
|
['i', 0],
|
||||||
|
['shall', 0],
|
||||||
|
['a', 0],
|
||||||
|
['for', 0],
|
||||||
|
['unto', 0],
|
||||||
|
['he', 0],
|
||||||
|
['in', 0],
|
||||||
|
['to', 0],
|
||||||
|
['that', 0],
|
||||||
|
['of', 0],
|
||||||
|
['and', 0],
|
||||||
|
['the', 0],
|
||||||
|
]);
|
||||||
private readonly dataPath = 'assets/data';
|
private readonly dataPath = 'assets/data';
|
||||||
|
|
||||||
constructor(private http: HttpClient, private localStorageService: StorageMap, private db: AngularFireDatabase) {
|
constructor(private http: HttpClient, private localStorageService: StorageMap, private db: AngularFireDatabase) {
|
||||||
@ -82,6 +148,9 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
|
|
||||||
async addCard(qry: string, nextToItem: CardItem = null) {
|
async addCard(qry: string, nextToItem: CardItem = null) {
|
||||||
const card = await this.getCardByQuery(qry);
|
const card = await this.getCardByQuery(qry);
|
||||||
|
if (!card) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.dispatch(AppActionFactory.newAddCard(card, nextToItem));
|
this.dispatch(AppActionFactory.newAddCard(card, nextToItem));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,6 +162,7 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
}
|
}
|
||||||
this.dispatch(
|
this.dispatch(
|
||||||
AppActionFactory.newUpdateCards({
|
AppActionFactory.newUpdateCards({
|
||||||
|
type: queries.type,
|
||||||
createdOn: queries.createdOn,
|
createdOn: queries.createdOn,
|
||||||
value: cards,
|
value: cards,
|
||||||
})
|
})
|
||||||
@ -103,6 +173,9 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
if (qry.startsWith('note:')) {
|
if (qry.startsWith('note:')) {
|
||||||
const id = qry.replace('note:', '');
|
const id = qry.replace('note:', '');
|
||||||
const data = this.getState().notes.value.find((o) => o.id === id);
|
const data = this.getState().notes.value.find((o) => o.id === id);
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
qry,
|
qry,
|
||||||
type: CardType.Note,
|
type: CardType.Note,
|
||||||
@ -111,6 +184,9 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
} else if (qry.search(/[0-9]/i) === -1) {
|
} else if (qry.search(/[0-9]/i) === -1) {
|
||||||
// // its a search term.
|
// // its a search term.
|
||||||
const data = await this.getWordsFromApi(qry);
|
const data = await this.getWordsFromApi(qry);
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
qry,
|
qry,
|
||||||
type: CardType.Word,
|
type: CardType.Word,
|
||||||
@ -126,6 +202,9 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
if (qry !== '') {
|
if (qry !== '') {
|
||||||
const myref = new BibleReference(qry.trim());
|
const myref = new BibleReference(qry.trim());
|
||||||
const data = await this.getPassageFromApi(myref.section);
|
const data = await this.getPassageFromApi(myref.section);
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
qry,
|
qry,
|
||||||
type: CardType.Passage,
|
type: CardType.Passage,
|
||||||
@ -263,6 +342,9 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
|
|
||||||
async getStrongsCard(strongsNumber: string, dict: StrongsDictionary) {
|
async getStrongsCard(strongsNumber: string, dict: StrongsDictionary) {
|
||||||
const result = await this.getStrongsFromApi(strongsNumber, dict);
|
const result = await this.getStrongsFromApi(strongsNumber, dict);
|
||||||
|
if (!result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const d = dict === 'grk' ? 'G' : 'H';
|
const d = dict === 'grk' ? 'G' : 'H';
|
||||||
|
|
||||||
const card: CardItem = {
|
const card: CardItem = {
|
||||||
@ -558,12 +640,19 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
await this.getStemWordIndex();
|
await this.getStemWordIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const excluded: string[] = [];
|
||||||
|
|
||||||
// now carry on...
|
// now carry on...
|
||||||
const qs = this.normalizeQueryString(qry);
|
const qs = this.normalizeQueryString(qry);
|
||||||
const results: (readonly string[])[] = [];
|
const results: (readonly string[])[] = [];
|
||||||
|
|
||||||
// Loop through each query term.
|
// Loop through each query term.
|
||||||
for (const q of qs) {
|
for (const q of qs) {
|
||||||
|
if (this.excludedWords.has(q)) {
|
||||||
|
excluded.push(q);
|
||||||
|
continue; // skip this word.
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.wordToStem.has(q)) {
|
if (!this.wordToStem.has(q)) {
|
||||||
this.dispatch({
|
this.dispatch({
|
||||||
type: 'UPDATE_ERROR',
|
type: 'UPDATE_ERROR',
|
||||||
@ -601,6 +690,18 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
// Now we need to test results. If there is more than one item in the array, we need to find the set
|
// Now we need to test results. If there is more than one item in the array, we need to find the set
|
||||||
// that is shared by all of them. IF not, we can just return those refs.
|
// that is shared by all of them. IF not, we can just return those refs.
|
||||||
if (results.length === 0) {
|
if (results.length === 0) {
|
||||||
|
if (excluded.length > 0) {
|
||||||
|
this.dispatch({
|
||||||
|
type: 'UPDATE_ERROR',
|
||||||
|
error: {
|
||||||
|
msg: `The ${excluded.length > 1 ? 'words' : 'word'} "${excluded.reduce((prev, curr, idx, arr) => {
|
||||||
|
return `${prev}, ${curr}`;
|
||||||
|
})}" ${excluded.length > 1 ? 'have' : 'has'} been excluded from search because it is too common.`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.dispatch({
|
this.dispatch({
|
||||||
type: 'UPDATE_ERROR',
|
type: 'UPDATE_ERROR',
|
||||||
error: {
|
error: {
|
||||||
@ -610,6 +711,18 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// // in this case, let the user know, but continue on because a result was found.
|
||||||
|
// if (excluded.length > 0) {
|
||||||
|
// this.dispatch({
|
||||||
|
// type: 'UPDATE_ERROR',
|
||||||
|
// error: {
|
||||||
|
// msg: `The ${excluded.length > 1 ? 'words' : 'word'} "${excluded.reduce((prev, curr, idx, arr) => {
|
||||||
|
// return `${prev}, ${curr}`;
|
||||||
|
// })}" ${excluded.length > 1 ? 'have' : 'has'} been excluded from search because it is too common.`,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
if (results.length === 1) {
|
if (results.length === 1) {
|
||||||
// we go down this path if only one word was searched for
|
// we go down this path if only one word was searched for
|
||||||
return {
|
return {
|
||||||
|
@ -8,7 +8,7 @@ import { Settings, User } from '../models/app-state';
|
|||||||
import { SavedPage } from '../models/page-state';
|
import { SavedPage } from '../models/page-state';
|
||||||
import { CardType, DataReference } from '../models/card-state';
|
import { CardType, DataReference } from '../models/card-state';
|
||||||
import { StorageService } from './storage.service';
|
import { StorageService } from './storage.service';
|
||||||
import { Storable } from '../common/storable';
|
import { Storable, StorableType } from '../common/storable';
|
||||||
import { NoteItem } from '../models/note-state';
|
import { NoteItem } from '../models/note-state';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@ -39,6 +39,7 @@ export class MigrationVersion0to1 {
|
|||||||
settings,
|
settings,
|
||||||
savedPages,
|
savedPages,
|
||||||
{
|
{
|
||||||
|
type: StorableType.modified,
|
||||||
createdOn: new Date(0).toISOString(),
|
createdOn: new Date(0).toISOString(),
|
||||||
value: [],
|
value: [],
|
||||||
},
|
},
|
||||||
|
@ -4,7 +4,7 @@ import { AngularFireDatabase, AngularFireObject } from '@angular/fire/database';
|
|||||||
import { DataSnapshot } from '@angular/fire/database/interfaces';
|
import { DataSnapshot } from '@angular/fire/database/interfaces';
|
||||||
|
|
||||||
import { SubscriberBase } from '../common/subscriber-base';
|
import { SubscriberBase } from '../common/subscriber-base';
|
||||||
import { IStorable, UserVersion } from '../common/storable';
|
import { IStorable, StorableType, UserVersion } from '../common/storable';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
import { MigrationVersion0to1 } from './migration0to1.service';
|
import { MigrationVersion0to1 } from './migration0to1.service';
|
||||||
|
|
||||||
@ -16,6 +16,36 @@ import { isNullOrUndefined } from '../common/helpers';
|
|||||||
import { DataReference } from '../models/card-state';
|
import { DataReference } from '../models/card-state';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class handles all the storage needs of the application. It handles both
|
||||||
|
* local and remote storage and syncing between the two.
|
||||||
|
*
|
||||||
|
* There are three components to the system that are important.
|
||||||
|
*
|
||||||
|
* 1) Listening for changes to the local data store (see the Local //#region)
|
||||||
|
*
|
||||||
|
* When data is written to the local data store, that data needs to be sent to
|
||||||
|
* the application state so the running storage reflects the local storage.
|
||||||
|
*
|
||||||
|
* The local storage is initialized in the app.component.ts constructor.
|
||||||
|
*
|
||||||
|
* 2) Listening for changes to the remote data store (see the Remote //#region)
|
||||||
|
*
|
||||||
|
* When data is written to the remote data store, that data needs to be sent to
|
||||||
|
* the application state so the running storage reflects the remote storage.
|
||||||
|
*
|
||||||
|
* The remote storage is initialized in the app.component.ts constructor once a user
|
||||||
|
* is available indicating that a connection has been established with the remote data
|
||||||
|
* store.
|
||||||
|
*
|
||||||
|
* 3) Listening for changes from the application state.
|
||||||
|
*
|
||||||
|
* When something happens in the application state, that state needs to be stored
|
||||||
|
* locally so the changes aren't lost, and remotely so the changes can be shared
|
||||||
|
* and persisten across devices and different installs.
|
||||||
|
*
|
||||||
|
* The listeners to the app state are initialized in the constructor.
|
||||||
|
*/
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
@ -48,6 +78,7 @@ export class StorageService extends SubscriberBase {
|
|||||||
(sync, cards) => ({
|
(sync, cards) => ({
|
||||||
syncCardsAcrossDevices: sync,
|
syncCardsAcrossDevices: sync,
|
||||||
currentCards: {
|
currentCards: {
|
||||||
|
type: cards.type,
|
||||||
createdOn: cards.createdOn,
|
createdOn: cards.createdOn,
|
||||||
value: cards.value.map((o) => {
|
value: cards.value.map((o) => {
|
||||||
return {
|
return {
|
||||||
@ -55,7 +86,7 @@ export class StorageService extends SubscriberBase {
|
|||||||
type: o.type,
|
type: o.type,
|
||||||
} as DataReference;
|
} as DataReference;
|
||||||
}),
|
}),
|
||||||
},
|
} as IStorable<DataReference[]>,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -67,73 +98,6 @@ export class StorageService extends SubscriberBase {
|
|||||||
private v0to1: MigrationVersion0to1
|
private v0to1: MigrationVersion0to1
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// handle remote and local storage
|
|
||||||
// when the specific items change in the state,
|
|
||||||
// store them locally and remotely, if you're logged in.
|
|
||||||
this.observeStorable('Display Settings', this.settingsState$, this.settingsPath, this.settingsRemoteObject);
|
|
||||||
this.observeStorable('Page', this.savedPagesState$, this.savedPagesPath, this.savedPagesRemoteObject);
|
|
||||||
this.observeStorable('Note', this.noteItemsState$, this.noteItemsPath, this.noteItemsRemoteObject);
|
|
||||||
|
|
||||||
// whenever the user setting or the current cards change, save the cards
|
|
||||||
// and if syncing is turned on, send them to the remote store.
|
|
||||||
this.addSubscription(
|
|
||||||
this.syncCurrentItems$.subscribe((v) => {
|
|
||||||
// set a local sync variable, so that the remote events know whether to
|
|
||||||
// accept remote state changes.
|
|
||||||
this.syncCurrentItems = v.syncCardsAcrossDevices;
|
|
||||||
|
|
||||||
// update local
|
|
||||||
this.local.set(this.cardsPath, v.currentCards).subscribe(
|
|
||||||
() => {
|
|
||||||
// nop
|
|
||||||
},
|
|
||||||
// error
|
|
||||||
() => {
|
|
||||||
// tslint:disable-next-line: quotemark
|
|
||||||
this.appService.dispatchError(`Something went wrong and the current items weren't saved. :(`);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// since you updated the local variable above, this remote update will
|
|
||||||
// get picked up by the remote subscription below.
|
|
||||||
if (v.syncCardsAcrossDevices) {
|
|
||||||
this.cardsRemoteObject.set(v.currentCards);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
observeStorable<T>(
|
|
||||||
name: string,
|
|
||||||
state$: Observable<IStorable<T>>,
|
|
||||||
path: string,
|
|
||||||
remoteObject: AngularFireObject<IStorable<T>>
|
|
||||||
) {
|
|
||||||
this.addSubscription(
|
|
||||||
state$.subscribe((data) => {
|
|
||||||
if (!data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update local
|
|
||||||
this.local.set(path, data).subscribe(
|
|
||||||
() => {
|
|
||||||
// nop
|
|
||||||
},
|
|
||||||
// error
|
|
||||||
() => {
|
|
||||||
// tslint:disable-next-line: quotemark
|
|
||||||
this.appService.dispatchError(`Something went wrong and the ${name} wasn't saved. :(`);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// update remote
|
|
||||||
if (remoteObject) {
|
|
||||||
remoteObject.set(data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//#region Remote
|
//#region Remote
|
||||||
@ -142,8 +106,9 @@ export class StorageService extends SubscriberBase {
|
|||||||
// do the necessary migration steps.
|
// do the necessary migration steps.
|
||||||
this.handleMigration(user);
|
this.handleMigration(user);
|
||||||
|
|
||||||
// initialize the remote store monitoring
|
// initialize the remote store monitoring. This is where we listen for changes on the remote location.
|
||||||
this.updateRemote<Settings>(
|
|
||||||
|
this.monitorRemote<Settings>(
|
||||||
this.settingsPath,
|
this.settingsPath,
|
||||||
user,
|
user,
|
||||||
(ro) => {
|
(ro) => {
|
||||||
@ -154,7 +119,7 @@ export class StorageService extends SubscriberBase {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.updateRemote<SavedPage[]>(
|
this.monitorRemote<SavedPage[]>(
|
||||||
this.savedPagesPath,
|
this.savedPagesPath,
|
||||||
user,
|
user,
|
||||||
(ro) => {
|
(ro) => {
|
||||||
@ -165,7 +130,7 @@ export class StorageService extends SubscriberBase {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.updateRemote<NoteItem[]>(
|
this.monitorRemote<NoteItem[]>(
|
||||||
this.noteItemsPath,
|
this.noteItemsPath,
|
||||||
user,
|
user,
|
||||||
(ro) => {
|
(ro) => {
|
||||||
@ -176,6 +141,8 @@ export class StorageService extends SubscriberBase {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// handle cards differently, because there is a setting that changes the behavior of how you handle
|
||||||
|
// new data from the remote.
|
||||||
this.cardsRemoteObject = this.remote.object<IStorable<DataReference[]>>(`/${this.cardsPath}/${user.uid}`);
|
this.cardsRemoteObject = this.remote.object<IStorable<DataReference[]>>(`/${this.cardsPath}/${user.uid}`);
|
||||||
this.addSubscription(
|
this.addSubscription(
|
||||||
this.cardsRemoteObject
|
this.cardsRemoteObject
|
||||||
@ -189,7 +156,7 @@ export class StorageService extends SubscriberBase {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateRemote<T>(
|
private monitorRemote<T>(
|
||||||
path: string,
|
path: string,
|
||||||
user: User,
|
user: User,
|
||||||
setRemoteObject: (remoteObject: AngularFireObject<IStorable<T>>) => void,
|
setRemoteObject: (remoteObject: AngularFireObject<IStorable<T>>) => void,
|
||||||
@ -205,6 +172,7 @@ export class StorageService extends SubscriberBase {
|
|||||||
.subscribe((remoteData) => {
|
.subscribe((remoteData) => {
|
||||||
if (!isNullOrUndefined(remoteData) && !isNullOrUndefined(remoteData.value)) {
|
if (!isNullOrUndefined(remoteData) && !isNullOrUndefined(remoteData.value)) {
|
||||||
// update the app state with remote data if it isn't null
|
// update the app state with remote data if it isn't null
|
||||||
|
console.log('Data recieved from remote store', remoteData);
|
||||||
action(remoteData);
|
action(remoteData);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -219,28 +187,149 @@ export class StorageService extends SubscriberBase {
|
|||||||
initialize the local stores
|
initialize the local stores
|
||||||
*/
|
*/
|
||||||
async initLocal() {
|
async initLocal() {
|
||||||
this.updateLocal<Settings>(this.settingsPath, (data) => {
|
this.getFromLocal<Settings>(this.settingsPath, (data) => {
|
||||||
this.appService.updateSettings(data);
|
this.appService.updateSettings(data);
|
||||||
});
|
});
|
||||||
this.updateLocal<SavedPage[]>(this.savedPagesPath, (data) => {
|
this.getFromLocal<SavedPage[]>(this.savedPagesPath, (data) => {
|
||||||
this.appService.updateSavedPages(data);
|
this.appService.updateSavedPages(data);
|
||||||
});
|
});
|
||||||
this.updateLocal<NoteItem[]>(this.noteItemsPath, (data) => {
|
this.getFromLocal<NoteItem[]>(this.noteItemsPath, (data) => {
|
||||||
this.appService.updateNotes(data);
|
this.appService.updateNotes(data);
|
||||||
});
|
});
|
||||||
this.updateLocal<DataReference[]>(this.cardsPath, (data) => {
|
this.getFromLocal<DataReference[]>(this.cardsPath, (data) => {
|
||||||
this.appService.updateCards(data);
|
this.appService.updateCards(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// we start listening after we get the local storage, to avoid overwriting local storage with the initial app state.
|
||||||
|
this.addAppStateListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateLocal<T>(path: string, action: (storable: IStorable<T>) => void) {
|
private async getFromLocal<T>(path: string, action: (storable: IStorable<T>) => void) {
|
||||||
const hasStorable = await this.local.has(path).toPromise();
|
const hasStorable = await this.local.has(path).toPromise();
|
||||||
if (hasStorable) {
|
if (hasStorable) {
|
||||||
const storable = (await this.local.get(path).toPromise()) as IStorable<T>;
|
const localData = (await this.local.get(path).toPromise()) as IStorable<T>;
|
||||||
action(storable);
|
console.log('Data recieved from local store', localData);
|
||||||
|
action(localData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private addAppStateListeners() {
|
||||||
|
// handle remote and local storage
|
||||||
|
// when the specific items change in the state,
|
||||||
|
// store them locally and remotely, if you're logged in.
|
||||||
|
this.addSubscription(
|
||||||
|
this.settingsState$.subscribe((data) => {
|
||||||
|
if (!data || data.type === StorableType.initial) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Data saved to local store', data);
|
||||||
|
// update local
|
||||||
|
this.local.set(this.settingsPath, data).subscribe(
|
||||||
|
() => {
|
||||||
|
// nop
|
||||||
|
},
|
||||||
|
// error
|
||||||
|
() => {
|
||||||
|
// tslint:disable-next-line: quotemark
|
||||||
|
this.appService.dispatchError(`Something went wrong and the Settings weren't saved. :(`);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// update remote
|
||||||
|
if (this.settingsRemoteObject) {
|
||||||
|
console.log('Data sent to remote store', data);
|
||||||
|
this.settingsRemoteObject.set(data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this.addSubscription(
|
||||||
|
this.savedPagesState$.subscribe((data) => {
|
||||||
|
if (!data || data.type === StorableType.initial) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Data saved to local store', data);
|
||||||
|
// update local
|
||||||
|
this.local.set(this.savedPagesPath, data).subscribe(
|
||||||
|
() => {
|
||||||
|
// nop
|
||||||
|
},
|
||||||
|
// error
|
||||||
|
() => {
|
||||||
|
// tslint:disable-next-line: quotemark
|
||||||
|
this.appService.dispatchError(`Something went wrong and the Page wasn't saved. :(`);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// update remote
|
||||||
|
if (this.savedPagesRemoteObject) {
|
||||||
|
console.log('Data sent to remote store', data);
|
||||||
|
this.savedPagesRemoteObject.set(data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this.addSubscription(
|
||||||
|
this.noteItemsState$.subscribe((data) => {
|
||||||
|
if (!data || data.type === StorableType.initial) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Data saved to local store', data);
|
||||||
|
// update local
|
||||||
|
this.local.set(this.noteItemsPath, data).subscribe(
|
||||||
|
() => {
|
||||||
|
// nop
|
||||||
|
},
|
||||||
|
// error
|
||||||
|
() => {
|
||||||
|
// tslint:disable-next-line: quotemark
|
||||||
|
this.appService.dispatchError(`Something went wrong and the Note wasn't saved. :(`);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// update remote
|
||||||
|
if (this.noteItemsRemoteObject) {
|
||||||
|
console.log('Data sent to remote store', data);
|
||||||
|
this.noteItemsRemoteObject.set(data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// whenever the user setting or the current cards change, save the cards
|
||||||
|
// and if syncing is turned on, send them to the remote store.
|
||||||
|
this.addSubscription(
|
||||||
|
this.syncCurrentItems$.subscribe((v) => {
|
||||||
|
// set a local sync variable, so that the remote events know whether to
|
||||||
|
// accept remote state changes.
|
||||||
|
this.syncCurrentItems = v.syncCardsAcrossDevices;
|
||||||
|
|
||||||
|
if (!v.currentCards || v.currentCards.type === StorableType.initial) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update local
|
||||||
|
this.local.set(this.cardsPath, v.currentCards).subscribe(
|
||||||
|
() => {
|
||||||
|
// nop
|
||||||
|
},
|
||||||
|
// error
|
||||||
|
() => {
|
||||||
|
// tslint:disable-next-line: quotemark
|
||||||
|
this.appService.dispatchError(`Something went wrong and the current cards weren't saved. :(`);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// since you updated the local variable above, this remote update will
|
||||||
|
// get picked up by the remote subscription below.
|
||||||
|
if (v.syncCardsAcrossDevices) {
|
||||||
|
this.cardsRemoteObject.set(v.currentCards);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region Migrations
|
//#region Migrations
|
||||||
|
Loading…
x
Reference in New Issue
Block a user