mirror of
https://gitlab.com/walljm/dynamicbible.git
synced 2025-07-23 23:39:50 -04:00
update the storage to do migrations
This commit is contained in:
parent
4d2faf0011
commit
23d997cdd8
@ -40,9 +40,7 @@ export class AppComponent extends SubscriberBase implements AfterViewInit {
|
|||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.storageService.initSavedPages();
|
this.storageService.initLocal();
|
||||||
this.storageService.initSettings();
|
|
||||||
this.storageService.initNotes();
|
|
||||||
|
|
||||||
this.addSubscription(
|
this.addSubscription(
|
||||||
this.error$.subscribe((err) => {
|
this.error$.subscribe((err) => {
|
||||||
|
3
src/src/app/common/helpers.ts
Normal file
3
src/src/app/common/helpers.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function isNullOrUndefined(obj: any) {
|
||||||
|
return obj === null || obj === undefined;
|
||||||
|
}
|
@ -12,3 +12,7 @@ export class Storable<T> implements IStorable<T> {
|
|||||||
createdOn: string;
|
createdOn: string;
|
||||||
value: T;
|
value: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UserVersion {
|
||||||
|
version: number;
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@ import { BibleReference, Overlap } from 'src/app/common/bible-reference';
|
|||||||
import { AppService } from 'src/app/services/app.service';
|
import { AppService } from 'src/app/services/app.service';
|
||||||
import { Paragraph, BiblePassageResult } from 'src/app/models/passage-state';
|
import { Paragraph, BiblePassageResult } from 'src/app/models/passage-state';
|
||||||
import { CardItem } from 'src/app/models/card-state';
|
import { CardItem } from 'src/app/models/card-state';
|
||||||
|
import { isNullOrUndefined } from 'src/app/common/helpers';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-passage-card',
|
selector: 'app-passage-card',
|
||||||
@ -21,7 +22,7 @@ export class PassageCardComponent extends CardComponent implements OnInit {
|
|||||||
|
|
||||||
// whenever the notes changes, look for any notes that reference this passage.
|
// whenever the notes changes, look for any notes that reference this passage.
|
||||||
notes$ = this.appService.select((state) =>
|
notes$ = this.appService.select((state) =>
|
||||||
state.notes.value !== null && this.ref !== undefined
|
!isNullOrUndefined(state.notes) && !isNullOrUndefined(state.notes.value) && !isNullOrUndefined(this.ref)
|
||||||
? state.notes.value.filter((o) => {
|
? state.notes.value.filter((o) => {
|
||||||
const refs = o.xref
|
const refs = o.xref
|
||||||
.split(';')
|
.split(';')
|
||||||
|
@ -7,7 +7,7 @@ import { HashTable } from '../common/hashtable';
|
|||||||
|
|
||||||
export interface AppState {
|
export interface AppState {
|
||||||
readonly currentSavedPage: SavedPage;
|
readonly currentSavedPage: SavedPage;
|
||||||
readonly currentCards: readonly DataReference[];
|
readonly currentCards: IStorable<readonly DataReference[]>;
|
||||||
|
|
||||||
readonly cardCache: HashTable<CardItem>;
|
readonly cardCache: HashTable<CardItem>;
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ import { getFromCardCache } from 'src/app/common/card-cache-operations';
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class SearchPage extends SubscriberBase implements OnInit {
|
export class SearchPage extends SubscriberBase implements OnInit {
|
||||||
cards$ = this.appService.select((state) => state.currentCards.map((o) => getFromCardCache(o, state.cardCache)));
|
cards$ = this.appService.select((state) => state.currentCards.value.map((o) => getFromCardCache(o, state.cardCache)));
|
||||||
suggestions$ = this.appService.select((state) => state.autocomplete);
|
suggestions$ = this.appService.select((state) => state.autocomplete);
|
||||||
|
|
||||||
savedPagedLoaded = false;
|
savedPagedLoaded = false;
|
||||||
|
@ -89,7 +89,7 @@ export class AppActionFactory {
|
|||||||
} as AppAction;
|
} as AppAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
static newUpdateCards(cards: CardItem[]): AppAction {
|
static newUpdateCards(cards: IStorable<CardItem[]>): AppAction {
|
||||||
return {
|
return {
|
||||||
type: 'UPDATE_CARDS',
|
type: 'UPDATE_CARDS',
|
||||||
cards,
|
cards,
|
||||||
@ -234,7 +234,7 @@ export type AppAction =
|
|||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'UPDATE_CARDS';
|
type: 'UPDATE_CARDS';
|
||||||
cards: CardItem[];
|
cards: IStorable<readonly CardItem[]>;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'UPDATE_ERROR';
|
type: 'UPDATE_ERROR';
|
||||||
|
@ -3,7 +3,10 @@ import { Overlap } from '../common/bible-reference';
|
|||||||
|
|
||||||
export const initialState: AppState = {
|
export const initialState: AppState = {
|
||||||
user: null,
|
user: null,
|
||||||
currentCards: [],
|
currentCards: {
|
||||||
|
createdOn: new Date(0).toISOString(),
|
||||||
|
value: [],
|
||||||
|
},
|
||||||
cardCache: {},
|
cardCache: {},
|
||||||
autocomplete: [],
|
autocomplete: [],
|
||||||
currentSavedPage: null,
|
currentSavedPage: null,
|
||||||
|
@ -32,7 +32,10 @@ describe('getNewestStorable', () => {
|
|||||||
describe('AppService Reducer', () => {
|
describe('AppService Reducer', () => {
|
||||||
const preState = {
|
const preState = {
|
||||||
user: null,
|
user: null,
|
||||||
currentCards: [],
|
currentCards: {
|
||||||
|
createdOn: new Date(0).toISOString(),
|
||||||
|
value: [],
|
||||||
|
},
|
||||||
cardCache: {},
|
cardCache: {},
|
||||||
autocomplete: [],
|
autocomplete: [],
|
||||||
currentSavedPage: {
|
currentSavedPage: {
|
||||||
@ -250,7 +253,7 @@ describe('AppService Reducer', () => {
|
|||||||
|
|
||||||
const action1 = AppActionFactory.newAddCard(card1, null);
|
const action1 = AppActionFactory.newAddCard(card1, null);
|
||||||
const testState = reducer(preState, action1);
|
const testState = reducer(preState, action1);
|
||||||
expect(testState.currentCards[0]).toBe(card1, 'Failed to add first card to empty list');
|
expect(testState.currentCards.value[0]).toBe(card1, 'Failed to add first card to empty list');
|
||||||
|
|
||||||
const action = AppActionFactory.newUpdateCurrentPage();
|
const action = AppActionFactory.newUpdateCurrentPage();
|
||||||
const testState2 = reducer(testState, action);
|
const testState2 = reducer(testState, action);
|
||||||
@ -311,7 +314,7 @@ describe('AppService Reducer', () => {
|
|||||||
|
|
||||||
const action1 = AppActionFactory.newAddCard(card1, null);
|
const action1 = AppActionFactory.newAddCard(card1, null);
|
||||||
const testState = reducer(preState, action1);
|
const testState = reducer(preState, action1);
|
||||||
expect(testState.currentCards[0]).toBe(card1, 'Failed to add first card to empty list');
|
expect(testState.currentCards.value[0]).toBe(card1, 'Failed to add first card to empty list');
|
||||||
|
|
||||||
const card2: CardItem = {
|
const card2: CardItem = {
|
||||||
qry: 'G123',
|
qry: 'G123',
|
||||||
@ -321,8 +324,8 @@ describe('AppService Reducer', () => {
|
|||||||
|
|
||||||
const action2 = AppActionFactory.newAddCard(card2, null);
|
const action2 = AppActionFactory.newAddCard(card2, null);
|
||||||
const testState2 = reducer(testState, action2);
|
const testState2 = reducer(testState, action2);
|
||||||
expect(testState2.currentCards.length).toBe(2, 'Failed to add second card to list with 1 item');
|
expect(testState2.currentCards.value.length).toBe(2, 'Failed to add second card to list with 1 item');
|
||||||
expect(testState2.currentCards[1]).toBe(card2);
|
expect(testState2.currentCards.value[1]).toBe(card2);
|
||||||
|
|
||||||
const card3: CardItem = {
|
const card3: CardItem = {
|
||||||
qry: 'G1234',
|
qry: 'G1234',
|
||||||
@ -357,8 +360,8 @@ describe('AppService Reducer', () => {
|
|||||||
|
|
||||||
const action3 = AppActionFactory.newAddCard(card3, card2);
|
const action3 = AppActionFactory.newAddCard(card3, card2);
|
||||||
const testState3 = reducer(setState, action3);
|
const testState3 = reducer(setState, action3);
|
||||||
expect(testState3.currentCards.length).toBe(3, 'Failed to add third card');
|
expect(testState3.currentCards.value.length).toBe(3, 'Failed to add third card');
|
||||||
expect(testState3.currentCards[1]).toBe(card3, 'Failed to insert card above the second card');
|
expect(testState3.currentCards.value[1]).toBe(card3, 'Failed to insert card above the second card');
|
||||||
|
|
||||||
// append to bottom, insert next to card
|
// append to bottom, insert next to card
|
||||||
|
|
||||||
@ -387,8 +390,8 @@ describe('AppService Reducer', () => {
|
|||||||
|
|
||||||
const action4 = AppActionFactory.newAddCard(card3, card1);
|
const action4 = AppActionFactory.newAddCard(card3, card1);
|
||||||
const testState4 = reducer(setState, action4);
|
const testState4 = reducer(setState, action4);
|
||||||
expect(testState4.currentCards.length).toBe(3, 'Failed to add third card');
|
expect(testState4.currentCards.value.length).toBe(3, 'Failed to add third card');
|
||||||
expect(testState4.currentCards[1]).toBe(card3, 'Failed to insert card below the first card');
|
expect(testState4.currentCards.value[1]).toBe(card3, 'Failed to insert card below the first card');
|
||||||
|
|
||||||
// append to bottom, do not insert next to card
|
// append to bottom, do not insert next to card
|
||||||
|
|
||||||
@ -417,8 +420,8 @@ describe('AppService Reducer', () => {
|
|||||||
|
|
||||||
const action5 = AppActionFactory.newAddCard(card3, card1);
|
const action5 = AppActionFactory.newAddCard(card3, card1);
|
||||||
const testState5 = reducer(setState, action5);
|
const testState5 = reducer(setState, action5);
|
||||||
expect(testState5.currentCards.length).toBe(3, 'Failed to add third card');
|
expect(testState5.currentCards.value.length).toBe(3, 'Failed to add third card');
|
||||||
expect(testState5.currentCards[2]).toBe(card3, 'Failed to insert card at end of the list');
|
expect(testState5.currentCards.value[2]).toBe(card3, 'Failed to insert card at end of the list');
|
||||||
|
|
||||||
// append to top, do not insert next to card
|
// append to top, do not insert next to card
|
||||||
|
|
||||||
@ -447,8 +450,8 @@ describe('AppService Reducer', () => {
|
|||||||
|
|
||||||
const action6 = AppActionFactory.newAddCard(card3, card1);
|
const action6 = AppActionFactory.newAddCard(card3, card1);
|
||||||
const testState6 = reducer(setState, action6);
|
const testState6 = reducer(setState, action6);
|
||||||
expect(testState6.currentCards.length).toBe(3, 'Failed to add third card');
|
expect(testState6.currentCards.value.length).toBe(3, 'Failed to add third card');
|
||||||
expect(testState6.currentCards[0]).toBe(card3, 'Failed to insert card at start of the list');
|
expect(testState6.currentCards.value[0]).toBe(card3, 'Failed to insert card at start of the list');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('UPDATE_CARD', () => {
|
it('UPDATE_CARD', () => {
|
||||||
@ -470,7 +473,7 @@ describe('AppService Reducer', () => {
|
|||||||
const action2 = AppActionFactory.newUpdateCard(newCard, oldCard);
|
const action2 = AppActionFactory.newUpdateCard(newCard, oldCard);
|
||||||
const testState = reducer(preState1, action2);
|
const testState = reducer(preState1, action2);
|
||||||
|
|
||||||
expect(testState.currentCards[0].qry).toBe('H88', 'Should update the card');
|
expect(testState.currentCards.value[0].qry).toBe('H88', 'Should update the card');
|
||||||
expect(testState.cardCache[getCardCacheKey(newCard)].qry).toBe('H88', 'Should exist in card cache');
|
expect(testState.cardCache[getCardCacheKey(newCard)].qry).toBe('H88', 'Should exist in card cache');
|
||||||
expect(testState.cardCache[getCardCacheKey(oldCard)]).toBeUndefined(
|
expect(testState.cardCache[getCardCacheKey(oldCard)]).toBeUndefined(
|
||||||
'Should be undefined, having been removed from card cache'
|
'Should be undefined, having been removed from card cache'
|
||||||
@ -490,8 +493,8 @@ describe('AppService Reducer', () => {
|
|||||||
const action2 = AppActionFactory.newRemoveCard(card);
|
const action2 = AppActionFactory.newRemoveCard(card);
|
||||||
const testState = reducer(preState1, action2);
|
const testState = reducer(preState1, action2);
|
||||||
|
|
||||||
expect(preState1.currentCards.length).toBe(1, 'Should have added the card in preparation for removing the card');
|
expect(preState1.currentCards.value.length).toBe(1, 'Should have added the card in preparation for removing the card');
|
||||||
expect(testState.currentCards.length).toBe(0, 'Should have removed the card');
|
expect(testState.currentCards.value.length).toBe(0, 'Should have removed the card');
|
||||||
expect(testState.cardCache[getCardCacheKey(card)]).toBeUndefined(
|
expect(testState.cardCache[getCardCacheKey(card)]).toBeUndefined(
|
||||||
'Should be undefined, having been removed from card cache'
|
'Should be undefined, having been removed from card cache'
|
||||||
);
|
);
|
||||||
@ -514,25 +517,25 @@ describe('AppService Reducer', () => {
|
|||||||
};
|
};
|
||||||
const action2 = AppActionFactory.newAddCard(card2, null);
|
const action2 = AppActionFactory.newAddCard(card2, null);
|
||||||
const preState2 = reducer(preState1, action2);
|
const preState2 = reducer(preState1, action2);
|
||||||
expect(preState2.currentCards.length).toBe(2, 'Should have two cards');
|
expect(preState2.currentCards.value.length).toBe(2, 'Should have two cards');
|
||||||
expect(preState2.currentCards[0].qry).toBe('G123');
|
expect(preState2.currentCards.value[0].qry).toBe('G123');
|
||||||
expect(preState2.currentCards[1].qry).toBe('H88');
|
expect(preState2.currentCards.value[1].qry).toBe('H88');
|
||||||
|
|
||||||
const action3 = AppActionFactory.newMoveCard(card2, MoveDirection.Up);
|
const action3 = AppActionFactory.newMoveCard(card2, MoveDirection.Up);
|
||||||
const testState1 = reducer(preState2, action3);
|
const testState1 = reducer(preState2, action3);
|
||||||
expect(testState1.currentCards[0].qry).toBe('H88');
|
expect(testState1.currentCards.value[0].qry).toBe('H88');
|
||||||
expect(testState1.currentCards[1].qry).toBe('G123');
|
expect(testState1.currentCards.value[1].qry).toBe('G123');
|
||||||
const testState4 = reducer(preState2, action3);
|
const testState4 = reducer(preState2, action3);
|
||||||
expect(testState4.currentCards[0].qry).toBe('H88');
|
expect(testState4.currentCards.value[0].qry).toBe('H88');
|
||||||
expect(testState4.currentCards[1].qry).toBe('G123');
|
expect(testState4.currentCards.value[1].qry).toBe('G123');
|
||||||
|
|
||||||
const action4 = AppActionFactory.newMoveCard(card1, MoveDirection.Down);
|
const action4 = AppActionFactory.newMoveCard(card1, MoveDirection.Down);
|
||||||
const testState2 = reducer(preState2, action4);
|
const testState2 = reducer(preState2, action4);
|
||||||
expect(testState2.currentCards[0].qry).toBe('H88');
|
expect(testState2.currentCards.value[0].qry).toBe('H88');
|
||||||
expect(testState2.currentCards[1].qry).toBe('G123');
|
expect(testState2.currentCards.value[1].qry).toBe('G123');
|
||||||
const testState3 = reducer(preState2, action4);
|
const testState3 = reducer(preState2, action4);
|
||||||
expect(testState3.currentCards[0].qry).toBe('H88');
|
expect(testState3.currentCards.value[0].qry).toBe('H88');
|
||||||
expect(testState3.currentCards[1].qry).toBe('G123');
|
expect(testState3.currentCards.value[1].qry).toBe('G123');
|
||||||
});
|
});
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
@ -575,11 +578,11 @@ describe('AppService Reducer', () => {
|
|||||||
|
|
||||||
const action3 = AppActionFactory.newGetNote('123456789', null);
|
const action3 = AppActionFactory.newGetNote('123456789', null);
|
||||||
const preState3 = reducer(preState2, action3);
|
const preState3 = reducer(preState2, action3);
|
||||||
expect(preState3.currentCards.length).toBe(1, 'Should have added the note card');
|
expect(preState3.currentCards.value.length).toBe(1, 'Should have added the note card');
|
||||||
|
|
||||||
const action4 = AppActionFactory.newGetNote('1234567890', null);
|
const action4 = AppActionFactory.newGetNote('1234567890', null);
|
||||||
const preState4 = reducer(preState3, action4);
|
const preState4 = reducer(preState3, action4);
|
||||||
expect(preState4.currentCards.length).toBe(2, 'Should have added the note card');
|
expect(preState4.currentCards.value.length).toBe(2, 'Should have added the note card');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('NOTES: Update Notes, Delete Notes, Find Notes, ', () => {
|
it('NOTES: Update Notes, Delete Notes, Find Notes, ', () => {
|
||||||
@ -602,11 +605,11 @@ describe('AppService Reducer', () => {
|
|||||||
|
|
||||||
const action2 = AppActionFactory.newFindNotes('note', null);
|
const action2 = AppActionFactory.newFindNotes('note', null);
|
||||||
const preState2 = reducer(preState1, action2);
|
const preState2 = reducer(preState1, action2);
|
||||||
expect(preState2.currentCards.length).toBe(2, 'Should have found two notes card');
|
expect(preState2.currentCards.value.length).toBe(2, 'Should have found two notes card');
|
||||||
|
|
||||||
const action3 = AppActionFactory.newDeleteNote(note1);
|
const action3 = AppActionFactory.newDeleteNote(note1);
|
||||||
const preState3 = reducer(preState2, action3);
|
const preState3 = reducer(preState2, action3);
|
||||||
expect(preState3.currentCards.length).toBe(1, 'Should have deleted the note card');
|
expect(preState3.currentCards.value.length).toBe(1, 'Should have deleted the note card');
|
||||||
expect(preState3.notes.value.length).toBe(1, 'Should have added the notes');
|
expect(preState3.notes.value.length).toBe(1, 'Should have added the notes');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
|||||||
...state,
|
...state,
|
||||||
// if the currentSavedPage was loaded, replace it with the info from the
|
// if the currentSavedPage was loaded, replace it with the info from the
|
||||||
// new savedPages array, as it might have changed.
|
// new savedPages array, as it might have changed.
|
||||||
currentCards: hasCurrentSavedPage ? currentSavedPage.queries : state.currentCards,
|
currentCards: hasCurrentSavedPage ? new Storable(currentSavedPage.queries) : state.currentCards,
|
||||||
currentSavedPage: hasCurrentSavedPage ? currentSavedPage : state.currentSavedPage,
|
currentSavedPage: hasCurrentSavedPage ? currentSavedPage : state.currentSavedPage,
|
||||||
savedPagesLoaded: true,
|
savedPagesLoaded: true,
|
||||||
savedPages, // update the savedPages
|
savedPages, // update the savedPages
|
||||||
@ -162,7 +162,7 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
|||||||
const current = {
|
const current = {
|
||||||
id: state.currentSavedPage.id,
|
id: state.currentSavedPage.id,
|
||||||
title: state.currentSavedPage.title,
|
title: state.currentSavedPage.title,
|
||||||
queries: [...mergeCardList(state.currentCards, state.settings.value.pageSettings.mergeStrategy)],
|
queries: [...mergeCardList(state.currentCards.value, state.settings.value.pageSettings.mergeStrategy)],
|
||||||
};
|
};
|
||||||
|
|
||||||
const savedPages = new Storable<SavedPage[]>([
|
const savedPages = new Storable<SavedPage[]>([
|
||||||
@ -186,7 +186,7 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
|||||||
// create a new saved page object
|
// create a new saved page object
|
||||||
title: action.title,
|
title: action.title,
|
||||||
id: UUID.UUID().toString(),
|
id: UUID.UUID().toString(),
|
||||||
queries: [...mergeCardList(state.currentCards, state.settings.value.pageSettings.mergeStrategy)],
|
queries: [...mergeCardList(state.currentCards.value, state.settings.value.pageSettings.mergeStrategy)],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -239,28 +239,28 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
|||||||
let cards = [];
|
let cards = [];
|
||||||
|
|
||||||
if (action.nextToItem && state.settings.value.displaySettings.insertCardNextToItem) {
|
if (action.nextToItem && state.settings.value.displaySettings.insertCardNextToItem) {
|
||||||
const idx = state.currentCards.indexOf(action.nextToItem);
|
const idx = state.currentCards.value.indexOf(action.nextToItem);
|
||||||
|
|
||||||
if (state.settings.value.displaySettings.appendCardToBottom) {
|
if (state.settings.value.displaySettings.appendCardToBottom) {
|
||||||
const before = state.currentCards.slice(0, idx + 1);
|
const before = state.currentCards.value.slice(0, idx + 1);
|
||||||
const after = state.currentCards.slice(idx + 1);
|
const after = state.currentCards.value.slice(idx + 1);
|
||||||
cards = [...before, action.card, ...after];
|
cards = [...before, action.card, ...after];
|
||||||
} else {
|
} else {
|
||||||
const before = state.currentCards.slice(0, idx);
|
const before = state.currentCards.value.slice(0, idx);
|
||||||
const after = state.currentCards.slice(idx);
|
const after = state.currentCards.value.slice(idx);
|
||||||
cards = [...before, action.card, ...after];
|
cards = [...before, action.card, ...after];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (state.settings.value.displaySettings.appendCardToBottom) {
|
if (state.settings.value.displaySettings.appendCardToBottom) {
|
||||||
cards = [...state.currentCards, action.card];
|
cards = [...state.currentCards.value, action.card];
|
||||||
} else {
|
} else {
|
||||||
cards = [action.card, ...state.currentCards];
|
cards = [action.card, ...state.currentCards.value];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
currentCards: cards,
|
currentCards: new Storable(cards),
|
||||||
cardCache: updateInCardCache(action.card, state.cardCache),
|
cardCache: updateInCardCache(action.card, state.cardCache),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -268,12 +268,14 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
|||||||
case 'UPDATE_CARD': {
|
case 'UPDATE_CARD': {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
currentCards: state.currentCards.map((c) => {
|
currentCards: new Storable(
|
||||||
if (c === action.oldCard) {
|
state.currentCards.value.map((c) => {
|
||||||
return action.newCard;
|
if (c === action.oldCard) {
|
||||||
}
|
return action.newCard;
|
||||||
return c;
|
}
|
||||||
}),
|
return c;
|
||||||
|
})
|
||||||
|
),
|
||||||
cardCache: updateInCardCache(action.newCard, removeFromCardCache(action.oldCard, state.cardCache)),
|
cardCache: updateInCardCache(action.newCard, removeFromCardCache(action.oldCard, state.cardCache)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -304,23 +306,23 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
|||||||
...state,
|
...state,
|
||||||
currentSavedPage,
|
currentSavedPage,
|
||||||
savedPages,
|
savedPages,
|
||||||
currentCards: [...state.currentCards.filter((c) => c !== action.card)],
|
currentCards: new Storable([...state.currentCards.value.filter((c) => c !== action.card)]),
|
||||||
cardCache: removeFromCardCache(action.card, state.cardCache),
|
cardCache: removeFromCardCache(action.card, state.cardCache),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'MOVE_CARD': {
|
case 'MOVE_CARD': {
|
||||||
const cards = moveItemUpOrDown(state.currentCards, action.card, action.direction);
|
const cards = moveItemUpOrDown(state.currentCards.value, action.card, action.direction);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
currentCards: cards,
|
currentCards: new Storable(cards),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'UPDATE_CARDS': {
|
case 'UPDATE_CARDS': {
|
||||||
let cardCache = { ...state.cardCache };
|
let cardCache = { ...state.cardCache };
|
||||||
for (const card of action.cards) {
|
for (const card of action.cards.value) {
|
||||||
cardCache = updateInCardCache(card, cardCache);
|
cardCache = updateInCardCache(card, cardCache);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@ -355,22 +357,22 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
|||||||
let cards = [] as DataReference[];
|
let cards = [] as DataReference[];
|
||||||
|
|
||||||
if (action.nextToItem && state.settings.value.displaySettings.insertCardNextToItem) {
|
if (action.nextToItem && state.settings.value.displaySettings.insertCardNextToItem) {
|
||||||
const idx = state.currentCards.indexOf(action.nextToItem);
|
const idx = state.currentCards.value.indexOf(action.nextToItem);
|
||||||
|
|
||||||
if (state.settings.value.displaySettings.appendCardToBottom) {
|
if (state.settings.value.displaySettings.appendCardToBottom) {
|
||||||
const before = state.currentCards.slice(0, idx + 1);
|
const before = state.currentCards.value.slice(0, idx + 1);
|
||||||
const after = state.currentCards.slice(idx + 1);
|
const after = state.currentCards.value.slice(idx + 1);
|
||||||
cards = [...before, ...notes, ...after];
|
cards = [...before, ...notes, ...after];
|
||||||
} else {
|
} else {
|
||||||
const before = state.currentCards.slice(0, idx);
|
const before = state.currentCards.value.slice(0, idx);
|
||||||
const after = state.currentCards.slice(idx);
|
const after = state.currentCards.value.slice(idx);
|
||||||
cards = [...before, ...notes, ...after];
|
cards = [...before, ...notes, ...after];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (state.settings.value.displaySettings.appendCardToBottom) {
|
if (state.settings.value.displaySettings.appendCardToBottom) {
|
||||||
cards = [...state.currentCards, ...notes];
|
cards = [...state.currentCards.value, ...notes];
|
||||||
} else {
|
} else {
|
||||||
cards = [...notes, ...state.currentCards];
|
cards = [...notes, ...state.currentCards.value];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,7 +383,7 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
currentCards: cards,
|
currentCards: new Storable(cards),
|
||||||
cardCache: cache,
|
cardCache: cache,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -404,7 +406,7 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
|||||||
case 'UPDATE_NOTES': {
|
case 'UPDATE_NOTES': {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
notes: action.notes,
|
notes: action.notes ? action.notes : new Storable([]),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,7 +426,7 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
|||||||
|
|
||||||
const newState = {
|
const newState = {
|
||||||
...state,
|
...state,
|
||||||
currentCards: [...state.currentCards], // you want to trigger an update to the cards if a card update is different.
|
currentCards: new Storable([...state.currentCards.value]), // you want to trigger an update to the cards if a card update is different.
|
||||||
notes,
|
notes,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -439,15 +441,15 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
|||||||
// so iterate through all of them and if you find the note
|
// so iterate through all of them and if you find the note
|
||||||
// in any of them, remove it
|
// in any of them, remove it
|
||||||
|
|
||||||
const card = state.currentCards.find((o) => o.qry === action.note.id);
|
const card = state.currentCards.value.find((o) => o.qry === action.note.id);
|
||||||
|
|
||||||
const cards = card
|
const cards = card
|
||||||
? [
|
? [
|
||||||
...state.currentCards.filter((o) => {
|
...state.currentCards.value.filter((o) => {
|
||||||
return o.type !== CardType.Note || o.qry !== action.note.id;
|
return o.type !== CardType.Note || o.qry !== action.note.id;
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
: state.currentCards; // if card is undefined, then it wasn't in the current card list.
|
: state.currentCards.value; // if card is undefined, then it wasn't in the current card list.
|
||||||
|
|
||||||
const notes = new Storable<NoteItem[]>([...state.notes.value.filter((o) => o.id !== action.note.id)]);
|
const notes = new Storable<NoteItem[]>([...state.notes.value.filter((o) => o.id !== action.note.id)]);
|
||||||
|
|
||||||
@ -464,7 +466,7 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
currentCards: cards,
|
currentCards: new Storable(cards),
|
||||||
notes,
|
notes,
|
||||||
savedPages,
|
savedPages,
|
||||||
cardCache: card
|
cardCache: card
|
||||||
|
@ -28,7 +28,7 @@ import { HashTable } from '../common/hashtable';
|
|||||||
import { MoveDirection } from '../common/move-direction';
|
import { MoveDirection } from '../common/move-direction';
|
||||||
import { reducer } from './app-state-reducer';
|
import { reducer } from './app-state-reducer';
|
||||||
import { initialState } from './app-state-initial-state';
|
import { initialState } from './app-state-initial-state';
|
||||||
import { CardItem, CardType } from '../models/card-state';
|
import { CardItem, CardType, DataReference } from '../models/card-state';
|
||||||
import { SavedPage } from '../models/page-state';
|
import { SavedPage } from '../models/page-state';
|
||||||
import { AppActionFactory } from './app-state-actions';
|
import { AppActionFactory } from './app-state-actions';
|
||||||
|
|
||||||
@ -85,6 +85,20 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
this.dispatch(AppActionFactory.newAddCard(card, nextToItem));
|
this.dispatch(AppActionFactory.newAddCard(card, nextToItem));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateCards(queries: IStorable<readonly DataReference[]>) {
|
||||||
|
const cards: CardItem[] = [];
|
||||||
|
for (const q of queries.value) {
|
||||||
|
const card = await this.getCardByQuery(q.qry);
|
||||||
|
cards.push(card);
|
||||||
|
}
|
||||||
|
this.dispatch(
|
||||||
|
AppActionFactory.newUpdateCards({
|
||||||
|
createdOn: queries.createdOn,
|
||||||
|
value: cards,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private async getCardByQuery(qry: string): Promise<CardItem> {
|
private async getCardByQuery(qry: string): Promise<CardItem> {
|
||||||
if (qry.startsWith('note:')) {
|
if (qry.startsWith('note:')) {
|
||||||
const id = qry.replace('note:', '');
|
const id = qry.replace('note:', '');
|
||||||
@ -133,7 +147,7 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
cards.push(await this.getCardByQuery(ref.qry));
|
cards.push(await this.getCardByQuery(ref.qry));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dispatch(AppActionFactory.newUpdateCards(cards));
|
this.dispatch(AppActionFactory.newUpdateCards(new Storable(cards)));
|
||||||
}
|
}
|
||||||
|
|
||||||
savePage(title: string) {
|
savePage(title: string) {
|
||||||
|
138
src/src/app/services/migration0to1.service.ts
Normal file
138
src/src/app/services/migration0to1.service.ts
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { AngularFireDatabase, AngularFireObject } from '@angular/fire/database';
|
||||||
|
import { DataSnapshot } from '@angular/fire/database/interfaces';
|
||||||
|
import { UUID } from 'angular2-uuid';
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
import { Overlap } from '../common/bible-reference';
|
||||||
|
import { Settings, User } from '../models/app-state';
|
||||||
|
import { SavedPage } from '../models/page-state';
|
||||||
|
import { CardType, DataReference } from '../models/card-state';
|
||||||
|
import { StorageService } from './storage.service';
|
||||||
|
import { Storable } from '../common/storable';
|
||||||
|
import { NoteItem } from '../models/note-state';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class MigrationVersion0to1 {
|
||||||
|
private settingsPath = 'settings'; // the very first settings
|
||||||
|
private settingsRemoteObject: AngularFireObject<Version0Settings>;
|
||||||
|
|
||||||
|
constructor(private remote: AngularFireDatabase, private appService: AppService) {}
|
||||||
|
|
||||||
|
migrate(user: User, storage: StorageService) {
|
||||||
|
this.settingsRemoteObject = this.remote.object<Version0Settings>(`/${this.settingsPath}/${user.uid}`);
|
||||||
|
this.settingsRemoteObject.query.once('value').then((data: DataSnapshot) => {
|
||||||
|
if (!data.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const v: Version0Settings = data.val();
|
||||||
|
if (v === null || v === undefined) {
|
||||||
|
return; // no settings to migrate.
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = this.toDisplaySettings(v);
|
||||||
|
const savedPages = this.toSavedPages(v);
|
||||||
|
const currentCards = this.toCurrentCards(v);
|
||||||
|
|
||||||
|
storage.setRemote(
|
||||||
|
settings,
|
||||||
|
savedPages,
|
||||||
|
{
|
||||||
|
createdOn: new Date(0).toISOString(),
|
||||||
|
value: [],
|
||||||
|
},
|
||||||
|
currentCards
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private toDisplaySettings(v0: Version0Settings) {
|
||||||
|
return new Storable({
|
||||||
|
displaySettings: {
|
||||||
|
showStrongsAsModal: v0.strongs_modal ? v0.strongs_modal : false,
|
||||||
|
appendCardToBottom: v0.append_to_bottom ? v0.append_to_bottom : true,
|
||||||
|
insertCardNextToItem: v0.insert_next_to_item ? v0.insert_next_to_item : true,
|
||||||
|
clearSearchAfterQuery: v0.clear_search_after_query ? v0.clear_search_after_query : true,
|
||||||
|
cardFontSize: v0.font_size ? v0.font_size : 12,
|
||||||
|
cardFontFamily: v0.font_family ? v0.font_family : 'PT Serif',
|
||||||
|
showVersesOnNewLine: v0.verses_on_new_line ? v0.verses_on_new_line : false,
|
||||||
|
showVerseNumbers: v0.show_verse_numbers ? v0.show_verse_numbers : false,
|
||||||
|
showParagraphs: v0.show_paragraphs ? v0.show_paragraphs : true,
|
||||||
|
showParagraphHeadings: v0.show_paragraph_headings ? v0.show_paragraph_headings : true,
|
||||||
|
syncCardsAcrossDevices: v0.sync_search_items ? v0.sync_search_items : false,
|
||||||
|
},
|
||||||
|
pageSettings: {
|
||||||
|
mergeStrategy: Overlap.Equal,
|
||||||
|
},
|
||||||
|
cardIcons: {
|
||||||
|
words: 'font_download',
|
||||||
|
passage: 'menu_book',
|
||||||
|
strongs: 'speaker_notes',
|
||||||
|
note: 'note',
|
||||||
|
savedPage: 'inbox',
|
||||||
|
},
|
||||||
|
} as Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private toSavedPages(v0: Version0Settings) {
|
||||||
|
return new Storable(
|
||||||
|
v0.saved_pages
|
||||||
|
? v0.saved_pages.map((p) => {
|
||||||
|
return {
|
||||||
|
queries: p.queries.map((q) => {
|
||||||
|
return {
|
||||||
|
qry: q.qry,
|
||||||
|
type: CardType[q.type],
|
||||||
|
} as DataReference;
|
||||||
|
}),
|
||||||
|
title: p.title,
|
||||||
|
id: UUID.UUID().toString(),
|
||||||
|
} as SavedPage;
|
||||||
|
})
|
||||||
|
: []
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private toCurrentCards(v0: Version0Settings) {
|
||||||
|
return new Storable(
|
||||||
|
v0.items
|
||||||
|
? v0.items.map((o) => {
|
||||||
|
return {
|
||||||
|
qry: o.qry,
|
||||||
|
type: CardType[o.type],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: []
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Version0Settings {
|
||||||
|
append_to_bottom: boolean;
|
||||||
|
clear_search_after_query: boolean;
|
||||||
|
font_size: number;
|
||||||
|
font_family: string;
|
||||||
|
insert_next_to_item: boolean;
|
||||||
|
show_paragraph_headings: boolean;
|
||||||
|
show_paragraphs: boolean;
|
||||||
|
show_verse_numbers: boolean;
|
||||||
|
strongs_modal: boolean;
|
||||||
|
sync_search_items: boolean;
|
||||||
|
uid: string;
|
||||||
|
username: string;
|
||||||
|
verses_on_new_line: boolean;
|
||||||
|
saved_pages: Version0SavedPage[];
|
||||||
|
items: Version0Item[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Version0SavedPage {
|
||||||
|
queries: Version0Item[];
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Version0Item {
|
||||||
|
dict: string;
|
||||||
|
qry: string;
|
||||||
|
type: string;
|
||||||
|
}
|
@ -1,184 +1,275 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { StorageMap } from '@ngx-pwa/local-storage';
|
import { StorageMap } from '@ngx-pwa/local-storage';
|
||||||
import { AngularFireDatabase, AngularFireObject } from '@angular/fire/database';
|
import { AngularFireDatabase, AngularFireObject } from '@angular/fire/database';
|
||||||
import { IStorable } from '../common/storable';
|
import { DataSnapshot } from '@angular/fire/database/interfaces';
|
||||||
import { AppService } from './app.service';
|
|
||||||
import { DisplaySettings, User, Settings } from '../models/app-state';
|
|
||||||
|
|
||||||
import { SubscriberBase } from '../common/subscriber-base';
|
import { SubscriberBase } from '../common/subscriber-base';
|
||||||
|
import { IStorable, UserVersion } from '../common/storable';
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
import { MigrationVersion0to1 } from './migration0to1.service';
|
||||||
|
|
||||||
|
import { User, Settings, AppState } from '../models/app-state';
|
||||||
import { NoteItem } from '../models/note-state';
|
import { NoteItem } from '../models/note-state';
|
||||||
import { SavedPage } from '../models/page-state';
|
import { SavedPage } from '../models/page-state';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { isNullOrUndefined } from '../common/helpers';
|
||||||
|
import { DataReference } from '../models/card-state';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class StorageService extends SubscriberBase {
|
export class StorageService extends SubscriberBase {
|
||||||
|
private version = 1;
|
||||||
|
private storeVersion = `v${this.version}/`;
|
||||||
|
private userVersionPath = `userVersion`;
|
||||||
|
private userVersionRemoteObject: AngularFireObject<UserVersion>;
|
||||||
|
|
||||||
private settingsState$ = this.appService.select((state) => state.settings);
|
private settingsState$ = this.appService.select((state) => state.settings);
|
||||||
private settingsPath = 'settings';
|
private settingsPath = `${this.storeVersion}settings`;
|
||||||
private settingsRemoteObject: AngularFireObject<IStorable<Settings>>;
|
private settingsRemoteObject: AngularFireObject<IStorable<Settings>>;
|
||||||
|
|
||||||
private savedPagesState$ = this.appService.select((state) => state.savedPages);
|
private savedPagesState$ = this.appService.select((state) => state.savedPages);
|
||||||
private savedPagesPath = 'savedPages';
|
private savedPagesPath = `${this.storeVersion}savedPages`;
|
||||||
private savedPagesRemoteObject: AngularFireObject<IStorable<readonly SavedPage[]>>;
|
private savedPagesRemoteObject: AngularFireObject<IStorable<readonly SavedPage[]>>;
|
||||||
|
|
||||||
private noteItemsState$ = this.appService.select((state) => state.notes);
|
private noteItemsState$ = this.appService.select((state) => state.notes);
|
||||||
private noteItemsPath = 'noteItems';
|
private noteItemsPath = `${this.storeVersion}noteItems`;
|
||||||
private noteItemsRemoteObject: AngularFireObject<IStorable<readonly NoteItem[]>>;
|
private noteItemsRemoteObject: AngularFireObject<IStorable<readonly NoteItem[]>>;
|
||||||
|
|
||||||
constructor(private local: StorageMap, private remote: AngularFireDatabase, private appService: AppService) {
|
private cardsPath = `${this.storeVersion}currentCards`;
|
||||||
|
private cardsRemoteObject: AngularFireObject<IStorable<readonly DataReference[]>>;
|
||||||
|
|
||||||
|
private syncCurrentItems = false;
|
||||||
|
private syncCurrentItems$ = this.appService.select(
|
||||||
|
createSelector(
|
||||||
|
(state: AppState) => state.settings.value.displaySettings.syncCardsAcrossDevices,
|
||||||
|
(state: AppState) => state.currentCards,
|
||||||
|
(sync, cards) => ({
|
||||||
|
syncCardsAcrossDevices: sync,
|
||||||
|
currentCards: {
|
||||||
|
createdOn: cards.createdOn,
|
||||||
|
value: cards.value.map((o) => {
|
||||||
|
return {
|
||||||
|
qry: o.qry,
|
||||||
|
type: o.type,
|
||||||
|
} as DataReference;
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private local: StorageMap,
|
||||||
|
private remote: AngularFireDatabase,
|
||||||
|
private appService: AppService,
|
||||||
|
private v0to1: MigrationVersion0to1
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
//#region handle remote and local storage
|
// handle remote and local storage
|
||||||
// when the specific items change in the state,
|
// when the specific items change in the state,
|
||||||
// store them locally and remotely, if you're logged in.
|
// 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.addSubscription(
|
||||||
this.settingsState$.subscribe((settings) => {
|
this.syncCurrentItems$.subscribe((v) => {
|
||||||
if (!settings || settings.createdOn === new Date(0).toISOString()) {
|
// set a local sync variable, so that the remote events know whether to
|
||||||
return;
|
// accept remote state changes.
|
||||||
}
|
this.syncCurrentItems = v.syncCardsAcrossDevices;
|
||||||
|
|
||||||
// update local
|
// update local
|
||||||
this.local.set(this.settingsPath, settings).subscribe(
|
this.local.set(this.cardsPath, v.currentCards).subscribe(
|
||||||
() => {
|
() => {
|
||||||
// nop
|
// nop
|
||||||
},
|
},
|
||||||
// error
|
// error
|
||||||
() => {
|
() => {
|
||||||
// tslint:disable-next-line: quotemark
|
// tslint:disable-next-line: quotemark
|
||||||
this.appService.dispatchError("Something went wrong and the display settings weren't saved. :(");
|
this.appService.dispatchError(`Something went wrong and the current items weren't saved. :(`);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// update remote
|
// since you updated the local variable above, this remote update will
|
||||||
if (this.settingsRemoteObject) {
|
// get picked up by the remote subscription below.
|
||||||
this.settingsRemoteObject.set(settings);
|
if (v.syncCardsAcrossDevices) {
|
||||||
|
this.cardsRemoteObject.set(v.currentCards);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.addSubscription(
|
|
||||||
this.savedPagesState$.subscribe((savedPages) => {
|
|
||||||
if (!savedPages) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update local
|
|
||||||
this.local.set(this.savedPagesPath, savedPages).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) {
|
|
||||||
this.savedPagesRemoteObject.set(savedPages);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
this.addSubscription(
|
|
||||||
this.noteItemsState$.subscribe((notes) => {
|
|
||||||
if (!notes) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update local
|
|
||||||
this.local.set(this.noteItemsPath, notes).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) {
|
|
||||||
this.noteItemsRemoteObject.set(notes);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
initRemote(user: User) {
|
initRemote(user: User) {
|
||||||
this.settingsRemoteObject = this.remote.object<IStorable<Settings>>(`/${this.settingsPath}/${user.uid}`);
|
// do the necessary migration steps.
|
||||||
this.savedPagesRemoteObject = this.remote.object<IStorable<SavedPage[]>>(`/${this.savedPagesPath}/${user.uid}`);
|
this.handleMigration(user);
|
||||||
this.noteItemsRemoteObject = this.remote.object<IStorable<NoteItem[]>>(`/${this.noteItemsPath}/${user.uid}`);
|
|
||||||
|
// initialize the remote store monitoring
|
||||||
|
this.updateRemote<Settings>(
|
||||||
|
this.settingsPath,
|
||||||
|
user,
|
||||||
|
(ro) => {
|
||||||
|
this.settingsRemoteObject = ro;
|
||||||
|
},
|
||||||
|
(data) => {
|
||||||
|
this.appService.updateSettings(data);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.updateRemote<SavedPage[]>(
|
||||||
|
this.savedPagesPath,
|
||||||
|
user,
|
||||||
|
(ro) => {
|
||||||
|
this.savedPagesRemoteObject = ro;
|
||||||
|
},
|
||||||
|
(data) => {
|
||||||
|
this.appService.updateSavedPages(data);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.updateRemote<NoteItem[]>(
|
||||||
|
this.noteItemsPath,
|
||||||
|
user,
|
||||||
|
(ro) => {
|
||||||
|
this.noteItemsRemoteObject = ro;
|
||||||
|
},
|
||||||
|
(data) => {
|
||||||
|
this.appService.updateNotes(data);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.cardsRemoteObject = this.remote.object<IStorable<DataReference[]>>(`/${this.cardsPath}/${user.uid}`);
|
||||||
|
this.addSubscription(
|
||||||
|
this.cardsRemoteObject
|
||||||
|
.valueChanges() // when the value changes
|
||||||
|
.subscribe((remoteData) => {
|
||||||
|
if (!isNullOrUndefined(remoteData) && !isNullOrUndefined(remoteData.value) && this.syncCurrentItems) {
|
||||||
|
// update the app state with remote data, but only if syncing is turned on.
|
||||||
|
this.appService.updateCards(remoteData);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateRemote<T>(
|
||||||
|
path: string,
|
||||||
|
user: User,
|
||||||
|
setRemoteObject: (remoteObject: AngularFireObject<IStorable<T>>) => void,
|
||||||
|
action: (storable) => void
|
||||||
|
) {
|
||||||
|
const remoteObject = this.remote.object<IStorable<T>>(`/${path}/${user.uid}`);
|
||||||
|
setRemoteObject(remoteObject);
|
||||||
|
|
||||||
// display settings
|
// display settings
|
||||||
this.addSubscription(
|
this.addSubscription(
|
||||||
this.settingsRemoteObject
|
remoteObject
|
||||||
.valueChanges() // when the value changes
|
.valueChanges() // when the value changes
|
||||||
.subscribe((remoteSettings) => {
|
.subscribe((remoteData) => {
|
||||||
if (remoteSettings) {
|
if (!isNullOrUndefined(remoteData) && !isNullOrUndefined(remoteData.value)) {
|
||||||
// update the display settings locally from remote if it isn't null
|
// update the app state with remote data if it isn't null
|
||||||
this.appService.updateSettings(remoteSettings);
|
action(remoteData);
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// saved pages
|
|
||||||
this.addSubscription(
|
|
||||||
this.savedPagesRemoteObject
|
|
||||||
.valueChanges() // when the saved pages have changed
|
|
||||||
.subscribe((remoteSavedPages) => {
|
|
||||||
if (remoteSavedPages) {
|
|
||||||
// update the saved pages locally from remote if it isn't null
|
|
||||||
this.appService.updateSavedPages(remoteSavedPages);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// note items
|
|
||||||
this.addSubscription(
|
|
||||||
this.noteItemsRemoteObject
|
|
||||||
.valueChanges() // when the saved pages have changed
|
|
||||||
.subscribe((remoteNoteItems) => {
|
|
||||||
if (remoteNoteItems) {
|
|
||||||
// update the saved pages locally from remote if it isn't null
|
|
||||||
this.appService.updateNotes(remoteNoteItems);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async initSettings() {
|
//#endregion
|
||||||
const hasDisplaySettings = await this.local.has(this.settingsPath).toPromise();
|
|
||||||
|
|
||||||
if (hasDisplaySettings) {
|
//#region Local
|
||||||
const settings = (await this.local.get(this.settingsPath).toPromise()) as IStorable<Settings>;
|
|
||||||
|
|
||||||
this.appService.updateSettings(settings);
|
/*
|
||||||
|
initialize the local stores
|
||||||
|
*/
|
||||||
|
async initLocal() {
|
||||||
|
this.updateLocal<Settings>(this.settingsPath, (data) => {
|
||||||
|
this.appService.updateSettings(data);
|
||||||
|
});
|
||||||
|
this.updateLocal<SavedPage[]>(this.savedPagesPath, (data) => {
|
||||||
|
this.appService.updateSavedPages(data);
|
||||||
|
});
|
||||||
|
this.updateLocal<NoteItem[]>(this.noteItemsPath, (data) => {
|
||||||
|
this.appService.updateNotes(data);
|
||||||
|
});
|
||||||
|
this.updateLocal<DataReference[]>(this.cardsPath, (data) => {
|
||||||
|
this.appService.updateCards(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async updateLocal<T>(path: string, action: (storable: IStorable<T>) => void) {
|
||||||
|
const hasStorable = await this.local.has(path).toPromise();
|
||||||
|
if (hasStorable) {
|
||||||
|
const storable = (await this.local.get(path).toPromise()) as IStorable<T>;
|
||||||
|
action(storable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async initSavedPages() {
|
//#endregion
|
||||||
const exists = await this.local.has(this.savedPagesPath).toPromise();
|
|
||||||
|
|
||||||
if (exists) {
|
//#region Migrations
|
||||||
const savedPages = (await this.local.get(this.savedPagesPath).toPromise()) as IStorable<SavedPage[]>;
|
|
||||||
|
|
||||||
this.appService.updateSavedPages(savedPages);
|
private handleMigration(user: User) {
|
||||||
}
|
this.userVersionRemoteObject = this.remote.object<UserVersion>(`/${this.userVersionPath}/${user.uid}`);
|
||||||
|
this.userVersionRemoteObject.query.once('value').then((data: DataSnapshot) => {
|
||||||
|
const v: UserVersion = data.val();
|
||||||
|
if (v === null || v === undefined || v.version < this.version) {
|
||||||
|
// perform the migration.
|
||||||
|
this.v0to1.migrate(user, this);
|
||||||
|
|
||||||
|
// update the version so the migration doesn't happen again.
|
||||||
|
this.userVersionRemoteObject.set({ version: 1 });
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async initNotes() {
|
public setRemote(
|
||||||
const exists = await this.local.has(this.noteItemsPath).toPromise();
|
settings: IStorable<Settings>,
|
||||||
|
savedPages: IStorable<SavedPage[]>,
|
||||||
if (exists) {
|
notes: IStorable<NoteItem[]>,
|
||||||
const notes = (await this.local.get(this.noteItemsPath).toPromise()) as IStorable<NoteItem[]>;
|
cards: IStorable<DataReference[]>
|
||||||
|
) {
|
||||||
this.appService.updateNotes(notes);
|
this.settingsRemoteObject.set(settings);
|
||||||
}
|
this.savedPagesRemoteObject.set(savedPages);
|
||||||
|
this.noteItemsRemoteObject.set(notes);
|
||||||
|
this.cardsRemoteObject.set(cards);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user