mirror of
https://gitlab.com/walljm/dynamicbible.git
synced 2025-07-23 07:19:50 -04:00
bug fixes from jerem'y merge
move reducer, appstate, actions, service into separate files to make it easier to navigate
This commit is contained in:
parent
fa1f1e9f19
commit
f25dc11ca9
@ -30,7 +30,9 @@
|
||||
"firebase",
|
||||
"@firebase/app",
|
||||
"@firebase/auth",
|
||||
"@firebase/database"
|
||||
"@firebase/database",
|
||||
"@firebase/util",
|
||||
"@firebase/component"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
|
@ -6,6 +6,7 @@
|
||||
"start": "ng serve",
|
||||
"build": "ng build --prod",
|
||||
"test": "ng test",
|
||||
"test-headless": "ng test --watch=false --browsers=ChromeHeadless",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e"
|
||||
},
|
||||
|
@ -833,6 +833,10 @@ export class BibleReference {
|
||||
return Overlap.None;
|
||||
}
|
||||
|
||||
if (left.toString() === right.toString()) {
|
||||
return Overlap.Equal;
|
||||
}
|
||||
|
||||
if (
|
||||
// left is subset of right
|
||||
(left.startIndex >= right.startIndex && left.endIndex <= right.endIndex) ||
|
||||
|
@ -1,28 +1,28 @@
|
||||
import { maybeMergeCards, mergeCardList } from './card-operations';
|
||||
import { mergeCardList } from './card-operations';
|
||||
import { BibleReference, Overlap } from './bible-reference';
|
||||
import { CardType } from '../models/app-state';
|
||||
|
||||
describe('Card Merging', () => {
|
||||
it('Should merge two equal cards', () => {
|
||||
let cardList = [
|
||||
const cardList = [
|
||||
{ type: CardType.Passage, qry: 'Genesis 1:1', data: null, dict: '' },
|
||||
{ type: CardType.Passage, qry: 'Genesis 1:1', data: null, dict: '' },
|
||||
];
|
||||
for (const strat of [Overlap.Equal, Overlap.Subset]) {
|
||||
let merged = mergeCardList(cardList, strat);
|
||||
const merged = mergeCardList(cardList, strat);
|
||||
expect(merged.length).toBe(1);
|
||||
expect(merged[0].qry).toEqual(new BibleReference('Genesis 1:1').toString());
|
||||
}
|
||||
});
|
||||
|
||||
it('Should merge two equal cards with one in the middle', () => {
|
||||
let cardList = [
|
||||
const cardList = [
|
||||
{ type: CardType.Passage, qry: 'Genesis 1:1', data: null, dict: '' },
|
||||
{ type: CardType.Passage, qry: 'Genesis 1:2', data: null, dict: '' },
|
||||
{ type: CardType.Passage, qry: 'Genesis 1:1', data: null, dict: '' },
|
||||
];
|
||||
for (const strat of [Overlap.Equal, Overlap.Subset]) {
|
||||
let merged = mergeCardList(cardList, strat);
|
||||
const merged = mergeCardList(cardList, strat);
|
||||
expect(merged.length).toBe(2);
|
||||
expect(merged[0].qry).toEqual(new BibleReference('Genesis 1:1').toString());
|
||||
expect(merged[1].qry).toEqual(new BibleReference('Genesis 1:2').toString());
|
||||
@ -30,25 +30,25 @@ describe('Card Merging', () => {
|
||||
});
|
||||
|
||||
it('Should merge two intersecting cards', () => {
|
||||
let cardList = [
|
||||
const cardList = [
|
||||
{ type: CardType.Passage, qry: 'Genesis 1:1-2', data: null, dict: '' },
|
||||
{ type: CardType.Passage, qry: 'Genesis 1:1', data: null, dict: '' },
|
||||
];
|
||||
for (const strat of [Overlap.Intersect, Overlap.Subset]) {
|
||||
let merged = mergeCardList(cardList, strat);
|
||||
const merged = mergeCardList(cardList, strat);
|
||||
expect(merged.length).toBe(1);
|
||||
expect(merged[0].qry).toEqual(new BibleReference('Genesis 1:1 - 2').toString());
|
||||
}
|
||||
});
|
||||
|
||||
it('Should merge two intersecting cards with one in the middle', () => {
|
||||
let cardList = [
|
||||
const cardList = [
|
||||
{ type: CardType.Passage, qry: 'Genesis 1:1-2', data: null, dict: '' },
|
||||
{ type: CardType.Passage, qry: 'Genesis 1:4', data: null, dict: '' },
|
||||
{ type: CardType.Passage, qry: 'Genesis 1:1', data: null, dict: '' },
|
||||
];
|
||||
for (const strat of [Overlap.Intersect, Overlap.Subset]) {
|
||||
let merged = mergeCardList(cardList, strat);
|
||||
const merged = mergeCardList(cardList, strat);
|
||||
expect(merged.length).toBe(2);
|
||||
expect(merged[0].qry).toEqual(new BibleReference('Genesis 1:1 - 2').toString());
|
||||
expect(merged[1].qry).toEqual(new BibleReference('Genesis 1:4').toString());
|
||||
|
@ -14,7 +14,7 @@ export function maybeMergeCards(leftCard: CardItem, rightCard: CardItem, strateg
|
||||
if (leftCard.type === CardType.Passage) {
|
||||
const leftRef = new BibleReference(leftCard.qry);
|
||||
const rightRef = new BibleReference(rightCard.qry);
|
||||
let mergedRef = BibleReference.mergeReference(leftRef, rightRef, strategy);
|
||||
const mergedRef = BibleReference.mergeReference(leftRef, rightRef, strategy);
|
||||
if (mergedRef !== null) {
|
||||
return {
|
||||
...leftCard,
|
||||
@ -29,11 +29,11 @@ export function maybeMergeCards(leftCard: CardItem, rightCard: CardItem, strateg
|
||||
}
|
||||
|
||||
export function mergeCardList(cardList: CardItem[], strategy: Overlap) {
|
||||
if (strategy == Overlap.None || cardList.length === 0) {
|
||||
if (strategy === Overlap.None || cardList.length === 0) {
|
||||
return [...cardList];
|
||||
}
|
||||
let cardListCopy = [...cardList];
|
||||
let removals = [];
|
||||
const cardListCopy = [...cardList];
|
||||
const removals = [];
|
||||
for (const cardIdx in cardListCopy) {
|
||||
if (cardIdx in removals) {
|
||||
continue;
|
||||
@ -41,7 +41,7 @@ export function mergeCardList(cardList: CardItem[], strategy: Overlap) {
|
||||
for (let compareIdx = Number(cardIdx) + 1; compareIdx < cardListCopy.length; compareIdx++) {
|
||||
const leftCard = cardListCopy[cardIdx];
|
||||
const rightCard = cardListCopy[compareIdx];
|
||||
let mergedCard = maybeMergeCards(leftCard, rightCard, strategy);
|
||||
const mergedCard = maybeMergeCards(leftCard, rightCard, strategy);
|
||||
if (mergedCard != null) {
|
||||
cardListCopy[cardIdx] = mergedCard;
|
||||
removals.push(compareIdx);
|
||||
|
@ -1,24 +0,0 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SearchPage } from './search.page';
|
||||
|
||||
describe('SearchComponent', () => {
|
||||
let component: SearchPage;
|
||||
let fixture: ComponentFixture<SearchPage>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SearchPage],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SearchPage);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,15 +1,16 @@
|
||||
import { UUID } from 'angular2-uuid';
|
||||
|
||||
import { AppState } from '../models/app-state';
|
||||
import { AppState, CardType } from '../models/app-state';
|
||||
import { PageTitles, PageIcons } from '../constants';
|
||||
import { Overlap } from '../common/bible-reference';
|
||||
|
||||
export const initialState: AppState = {
|
||||
user: null,
|
||||
cards: [
|
||||
{
|
||||
qry: '',
|
||||
qry: 'UUIDGOESHERE',
|
||||
dict: 'n/a',
|
||||
type: 'Note',
|
||||
type: CardType.Note,
|
||||
data: {
|
||||
id: UUID.UUID(),
|
||||
xref: '1 pe 2:16; jn 3:16',
|
||||
@ -50,6 +51,12 @@ export const initialState: AppState = {
|
||||
syncCardsAcrossDevices: false,
|
||||
},
|
||||
},
|
||||
pageSettings: {
|
||||
createdOn: new Date(0).toISOString(),
|
||||
value: {
|
||||
mergeStrategy: Overlap.Equal,
|
||||
},
|
||||
},
|
||||
cardIcons: {
|
||||
words: 'font_download',
|
||||
passage: 'menu_book',
|
||||
|
@ -1,26 +1,15 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { reducer } from './app-state-reducer';
|
||||
import { initialState } from './app-state-initial-state';
|
||||
import { UUID } from 'angular2-uuid';
|
||||
import { PageTitles, PageIcons } from '../constants';
|
||||
import { AppAction } from './app-state-actions';
|
||||
import { CardItem, CardType } from '../models/app-state';
|
||||
import { Overlap } from '../common/bible-reference';
|
||||
|
||||
describe('AppService Reducer', () => {
|
||||
const preState = {
|
||||
user: null,
|
||||
cards: [
|
||||
{
|
||||
qry: '',
|
||||
dict: 'n/a',
|
||||
type: 'Note',
|
||||
data: {
|
||||
id: UUID.UUID(),
|
||||
xref: '1 pe 2:16; jn 3:16',
|
||||
title: 'Title Here',
|
||||
content: '# Content Here\nIn Markdown format.',
|
||||
},
|
||||
},
|
||||
],
|
||||
cards: [],
|
||||
autocomplete: [],
|
||||
currentSavedPage: null,
|
||||
savedPages: {
|
||||
@ -32,10 +21,7 @@ describe('AppService Reducer', () => {
|
||||
value: [],
|
||||
},
|
||||
savedPagesLoaded: false,
|
||||
mainPages: [
|
||||
{ title: PageTitles.Search, icon: PageIcons.Search, route: 'search' },
|
||||
{ title: PageTitles.Help, icon: PageIcons.Help, route: 'help' },
|
||||
],
|
||||
mainPages: [],
|
||||
error: null,
|
||||
displaySettings: {
|
||||
createdOn: new Date(0).toISOString(),
|
||||
@ -53,6 +39,12 @@ describe('AppService Reducer', () => {
|
||||
syncCardsAcrossDevices: false,
|
||||
},
|
||||
},
|
||||
pageSettings: {
|
||||
createdOn: new Date(0).toISOString(),
|
||||
value: {
|
||||
mergeStrategy: Overlap.Subset,
|
||||
},
|
||||
},
|
||||
cardIcons: {
|
||||
words: 'font_download',
|
||||
passage: 'menu_book',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { UUID } from 'angular2-uuid';
|
||||
|
||||
import { AppState, SavedPage, DisplaySettings } from '../models/app-state';
|
||||
import { AppState, SavedPage, DisplaySettings, CardType, CardItem } from '../models/app-state';
|
||||
import { IStorable, Storable } from '../models/storable';
|
||||
import { NoteItem } from '../models/note-state';
|
||||
|
||||
@ -9,6 +9,21 @@ import { ListDirection } from '../common/list-direction';
|
||||
import { AppAction } from './app-state-actions';
|
||||
import { initialState } from './app-state-initial-state';
|
||||
|
||||
function maybeMutateStorable<T>(
|
||||
state: AppState,
|
||||
candidate: IStorable<T>,
|
||||
incumbant: IStorable<T>,
|
||||
composeState: (item: IStorable<T>) => AppState
|
||||
): AppState {
|
||||
// only update if the settings are newer.
|
||||
if (new Date(candidate.createdOn) > new Date(incumbant.createdOn)) {
|
||||
return composeState(candidate);
|
||||
}
|
||||
|
||||
// candidate didn't win. return state untouched.
|
||||
return state;
|
||||
}
|
||||
|
||||
export function reducer(state: AppState, action: AppAction): AppState {
|
||||
// somtimes the state is null. lets not complain if that happens.
|
||||
if (state === undefined) {
|
||||
@ -72,7 +87,7 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
||||
{
|
||||
id: state.currentSavedPage.id,
|
||||
title: state.currentSavedPage.title,
|
||||
queries: [...state.cards],
|
||||
queries: [...state.cards], // need a function that processes the cards to merge references.
|
||||
},
|
||||
]);
|
||||
|
||||
@ -88,7 +103,7 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
||||
// create a new saved page object
|
||||
title: action.title,
|
||||
id: UUID.UUID().toString(),
|
||||
queries: [...state.cards],
|
||||
queries: [...state.cards], // need a function that processes the cards to merge references.
|
||||
},
|
||||
]);
|
||||
|
||||
@ -217,12 +232,12 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
||||
return {
|
||||
qry: o.id,
|
||||
dict: 'n/a',
|
||||
type: 'Note',
|
||||
type: CardType.Note,
|
||||
data: o,
|
||||
};
|
||||
});
|
||||
|
||||
let cards = [];
|
||||
let cards = [] as CardItem[];
|
||||
|
||||
if (action.nextToItem && state.displaySettings.value.insertCardNextToItem) {
|
||||
const idx = state.cards.indexOf(action.nextToItem);
|
||||
@ -253,7 +268,7 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
||||
const card = {
|
||||
qry: note.id,
|
||||
dict: 'n/a',
|
||||
type: 'Note',
|
||||
type: CardType.Note,
|
||||
data: note,
|
||||
};
|
||||
|
||||
@ -358,18 +373,3 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function maybeMutateStorable<T>(
|
||||
state: AppState,
|
||||
candidate: IStorable<T>,
|
||||
incumbant: IStorable<T>,
|
||||
composeState: (item: IStorable<T>) => AppState
|
||||
): AppState {
|
||||
// only update if the settings are newer.
|
||||
if (new Date(candidate.createdOn) > new Date(incumbant.createdOn)) {
|
||||
return composeState(candidate);
|
||||
}
|
||||
|
||||
// candidate didn't win. return state untouched.
|
||||
return state;
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AppService } from './app.service';
|
||||
|
||||
describe('AppService', () => {
|
||||
let service: AppService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(AppService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,13 +1,11 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AppState, SavedPage, Error, CardItem, DisplaySettings, User, CardType } from '../models/app-state';
|
||||
import { SavedPage, CardItem, DisplaySettings, User, CardType } from '../models/app-state';
|
||||
import { Section, BibleReference, Overlap } from '../common/bible-reference';
|
||||
import { PageTitles, PageIcons } from '../constants';
|
||||
import { createStateService } from '../common/state-service';
|
||||
import { UUID } from 'angular2-uuid';
|
||||
import { StorageMap } from '@ngx-pwa/local-storage';
|
||||
import { AngularFireDatabase } from '@angular/fire/database';
|
||||
import { IStorable, Storable } from '../models/storable';
|
||||
import { IStorable } from '../models/storable';
|
||||
import { NoteItem } from '../models/note-state';
|
||||
import { Paragraph, BiblePassage, BibleVerse, BibleParagraphPassage, BibleParagraph } from '../models/passage-state';
|
||||
import {
|
||||
@ -20,523 +18,8 @@ import {
|
||||
import { WordToStem, IndexResult, WordLookupResult } from '../models/words-state';
|
||||
import { HashTable } from '../common/hashtable';
|
||||
import { ListDirection } from '../common/list-direction';
|
||||
|
||||
const initialState: AppState = {
|
||||
user: null,
|
||||
cards: [
|
||||
{
|
||||
qry: 'UUIDGOESHERE',
|
||||
dict: 'n/a',
|
||||
type: CardType.Note,
|
||||
data: {
|
||||
id: UUID.UUID(),
|
||||
xref: '1 pe 2:16; jn 3:16',
|
||||
title: 'Title Here',
|
||||
content: '# Content Here\nIn Markdown format.',
|
||||
},
|
||||
},
|
||||
],
|
||||
autocomplete: [],
|
||||
currentSavedPage: null,
|
||||
savedPages: {
|
||||
createdOn: new Date(0).toISOString(),
|
||||
value: [],
|
||||
},
|
||||
notes: {
|
||||
createdOn: new Date(0).toISOString(),
|
||||
value: [],
|
||||
},
|
||||
savedPagesLoaded: false,
|
||||
mainPages: [
|
||||
{ title: PageTitles.Search, icon: PageIcons.Search, route: 'search' },
|
||||
{ title: PageTitles.Help, icon: PageIcons.Help, route: 'help' },
|
||||
],
|
||||
error: null,
|
||||
displaySettings: {
|
||||
createdOn: new Date(0).toISOString(),
|
||||
value: {
|
||||
showStrongsAsModal: false,
|
||||
appendCardToBottom: true,
|
||||
insertCardNextToItem: true,
|
||||
clearSearchAfterQuery: true,
|
||||
cardFontSize: 12,
|
||||
cardFontFamily: 'PT Serif',
|
||||
showVersesOnNewLine: false,
|
||||
showVerseNumbers: false,
|
||||
showParagraphs: true,
|
||||
showParagraphHeadings: true,
|
||||
syncCardsAcrossDevices: false,
|
||||
},
|
||||
},
|
||||
pageSettings: {
|
||||
createdOn: new Date(0).toISOString(),
|
||||
value: {
|
||||
mergeStrategy: Overlap.Subset,
|
||||
},
|
||||
},
|
||||
cardIcons: {
|
||||
words: 'font_download',
|
||||
passage: 'menu_book',
|
||||
strongs: 'speaker_notes',
|
||||
note: 'text_snippet',
|
||||
},
|
||||
};
|
||||
|
||||
//#region
|
||||
|
||||
type AppAction =
|
||||
| {
|
||||
type: 'GET_SAVED_PAGE';
|
||||
pageId: string;
|
||||
}
|
||||
| {
|
||||
type: 'SAVE_PAGE';
|
||||
title: string;
|
||||
}
|
||||
| {
|
||||
type: 'UPDATE_CURRENT_PAGE';
|
||||
}
|
||||
| {
|
||||
type: 'UPDATE_SAVED_PAGES';
|
||||
savedPages: IStorable<readonly SavedPage[]>;
|
||||
}
|
||||
| {
|
||||
type: 'ADD_CARD_TO_SAVED_PAGE';
|
||||
card: CardItem;
|
||||
pageId: string;
|
||||
}
|
||||
| {
|
||||
type: 'ADD_CARD';
|
||||
card: CardItem;
|
||||
nextToItem: CardItem;
|
||||
}
|
||||
| {
|
||||
type: 'UPDATE_CARD';
|
||||
newCard: CardItem;
|
||||
oldCard: CardItem;
|
||||
}
|
||||
| {
|
||||
type: 'REMOVE_CARD';
|
||||
card: CardItem;
|
||||
}
|
||||
| {
|
||||
type: 'MOVE_CARD';
|
||||
card: CardItem;
|
||||
direction: ListDirection;
|
||||
}
|
||||
| {
|
||||
type: 'UPDATE_ERROR';
|
||||
error: Error;
|
||||
}
|
||||
| {
|
||||
type: 'UPDATE_FONT_SIZE';
|
||||
cardFontSize: number;
|
||||
}
|
||||
| {
|
||||
type: 'UPDATE_FONT_FAMILY';
|
||||
cardFontFamily: string;
|
||||
}
|
||||
| {
|
||||
type: 'UPDATE_AUTOCOMPLETE';
|
||||
words: string[];
|
||||
}
|
||||
| {
|
||||
type: 'UPDATE_DISPLAY_SETTINGS';
|
||||
settings: IStorable<DisplaySettings>;
|
||||
}
|
||||
| {
|
||||
type: 'SET_USER';
|
||||
user: User;
|
||||
}
|
||||
| {
|
||||
type: 'FIND_NOTES';
|
||||
qry: string;
|
||||
nextToItem: CardItem;
|
||||
}
|
||||
| {
|
||||
type: 'GET_NOTE';
|
||||
noteId: string;
|
||||
nextToItem: CardItem;
|
||||
}
|
||||
| {
|
||||
type: 'UPDATE_NOTES';
|
||||
notes: IStorable<readonly NoteItem[]>;
|
||||
}
|
||||
| {
|
||||
type: 'SAVE_NOTE';
|
||||
note: NoteItem;
|
||||
}
|
||||
| {
|
||||
type: 'DELETE_NOTE';
|
||||
note: NoteItem;
|
||||
};
|
||||
|
||||
//#endregion Actions
|
||||
|
||||
function maybeMutateStorable<T>(
|
||||
state: AppState,
|
||||
candidate: IStorable<T>,
|
||||
incumbant: IStorable<T>,
|
||||
composeState: (item: IStorable<T>) => AppState
|
||||
): AppState {
|
||||
// only update if the settings are newer.
|
||||
if (new Date(candidate.createdOn) > new Date(incumbant.createdOn)) {
|
||||
return composeState(candidate);
|
||||
}
|
||||
|
||||
// candidate didn't win. return state untouched.
|
||||
return state;
|
||||
}
|
||||
|
||||
function reducer(state: AppState, action: AppAction): AppState {
|
||||
// somtimes the state is null. lets not complain if that happens.
|
||||
if (state === undefined) {
|
||||
state = initialState;
|
||||
}
|
||||
|
||||
switch (action.type) {
|
||||
//#region Display Settings
|
||||
|
||||
case 'UPDATE_DISPLAY_SETTINGS': {
|
||||
return maybeMutateStorable(state, action.settings, state.displaySettings, (item) => {
|
||||
return {
|
||||
...state,
|
||||
displaySettings: item,
|
||||
};
|
||||
});
|
||||
}
|
||||
case 'UPDATE_FONT_SIZE': {
|
||||
const settings = new Storable<DisplaySettings>({
|
||||
...state.displaySettings.value,
|
||||
cardFontSize: action.cardFontSize,
|
||||
});
|
||||
|
||||
return reducer(state, {
|
||||
type: 'UPDATE_DISPLAY_SETTINGS',
|
||||
settings,
|
||||
});
|
||||
}
|
||||
case 'UPDATE_FONT_FAMILY': {
|
||||
const settings = new Storable<DisplaySettings>({
|
||||
...state.displaySettings.value,
|
||||
cardFontFamily: action.cardFontFamily,
|
||||
});
|
||||
|
||||
return reducer(state, {
|
||||
type: 'UPDATE_DISPLAY_SETTINGS',
|
||||
settings,
|
||||
});
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
case 'UPDATE_AUTOCOMPLETE': {
|
||||
return {
|
||||
...state,
|
||||
autocomplete: [...action.words],
|
||||
};
|
||||
}
|
||||
case 'UPDATE_SAVED_PAGES': {
|
||||
return maybeMutateStorable(state, action.savedPages, state.savedPages, (item) => {
|
||||
return {
|
||||
...state,
|
||||
savedPagesLoaded: true,
|
||||
savedPages: item,
|
||||
};
|
||||
});
|
||||
}
|
||||
case 'UPDATE_CURRENT_PAGE': {
|
||||
const savedPages = new Storable<SavedPage[]>([
|
||||
...state.savedPages.value.filter((o) => o.id !== state.currentSavedPage.id),
|
||||
{
|
||||
id: state.currentSavedPage.id,
|
||||
title: state.currentSavedPage.title,
|
||||
queries: [...state.cards], // need a function that processes the cards to merge references.
|
||||
},
|
||||
]);
|
||||
|
||||
return reducer(state, {
|
||||
type: 'UPDATE_SAVED_PAGES',
|
||||
savedPages,
|
||||
});
|
||||
}
|
||||
case 'SAVE_PAGE': {
|
||||
const savedPages = new Storable([
|
||||
...state.savedPages.value,
|
||||
{
|
||||
// create a new saved page object
|
||||
title: action.title,
|
||||
id: UUID.UUID().toString(),
|
||||
queries: [...state.cards], // need a function that processes the cards to merge references.
|
||||
},
|
||||
]);
|
||||
|
||||
return reducer(state, {
|
||||
type: 'UPDATE_SAVED_PAGES',
|
||||
savedPages,
|
||||
});
|
||||
}
|
||||
case 'GET_SAVED_PAGE': {
|
||||
const page = state.savedPages.value.find((o) => o.id.toString() === action.pageId);
|
||||
|
||||
if (!page) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
currentSavedPage: page,
|
||||
cards: [...page.queries],
|
||||
};
|
||||
}
|
||||
case 'ADD_CARD_TO_SAVED_PAGE': {
|
||||
const savedPages = new Storable([
|
||||
...state.savedPages.value.map((o) => {
|
||||
if (o.id.toString() === action.pageId) {
|
||||
let cards = [];
|
||||
if (state.displaySettings.value.appendCardToBottom) {
|
||||
cards = [...o.queries, action.card];
|
||||
} else {
|
||||
cards = [action.card, ...o.queries];
|
||||
}
|
||||
return {
|
||||
...o,
|
||||
queries: cards,
|
||||
};
|
||||
}
|
||||
return o;
|
||||
}),
|
||||
]);
|
||||
|
||||
return reducer(state, {
|
||||
type: 'UPDATE_SAVED_PAGES',
|
||||
savedPages,
|
||||
});
|
||||
}
|
||||
case 'ADD_CARD': {
|
||||
let cards = [];
|
||||
|
||||
if (action.nextToItem && state.displaySettings.value.insertCardNextToItem) {
|
||||
const idx = state.cards.indexOf(action.nextToItem);
|
||||
|
||||
if (state.displaySettings.value.appendCardToBottom) {
|
||||
const before = state.cards.slice(0, idx + 1);
|
||||
const after = state.cards.slice(idx + 1);
|
||||
cards = [...before, action.card, ...after];
|
||||
} else {
|
||||
const before = state.cards.slice(0, idx);
|
||||
const after = state.cards.slice(idx);
|
||||
cards = [...before, action.card, ...after];
|
||||
}
|
||||
} else {
|
||||
if (state.displaySettings.value.appendCardToBottom) {
|
||||
cards = [...state.cards, action.card];
|
||||
} else {
|
||||
cards = [action.card, ...state.cards];
|
||||
}
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
cards,
|
||||
};
|
||||
}
|
||||
case 'UPDATE_CARD': {
|
||||
return {
|
||||
...state,
|
||||
cards: state.cards.map((c) => {
|
||||
if (c === action.oldCard) {
|
||||
return action.newCard;
|
||||
}
|
||||
return c;
|
||||
}),
|
||||
};
|
||||
}
|
||||
case 'REMOVE_CARD': {
|
||||
return {
|
||||
...state,
|
||||
cards: [...state.cards.filter((c) => c !== action.card)],
|
||||
};
|
||||
}
|
||||
case 'MOVE_CARD': {
|
||||
let cards = [];
|
||||
const idx = state.cards.indexOf(action.card);
|
||||
|
||||
if (
|
||||
(idx === 0 && action.direction === ListDirection.Up) || // can't go up if you're at the top
|
||||
(idx === state.cards.length - 1 && action.direction === ListDirection.Down) // can't go down if you're at the bottom
|
||||
) {
|
||||
// you can't go up.
|
||||
return state;
|
||||
}
|
||||
|
||||
const before = state.cards.slice(0, idx);
|
||||
const after = state.cards.slice(idx + 1);
|
||||
|
||||
if (action.direction === ListDirection.Down) {
|
||||
cards = [...before, after[0], action.card, ...after.slice(1)];
|
||||
} else {
|
||||
cards = [...before.slice(0, before.length - 1), action.card, before[before.length - 1], ...after];
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
cards,
|
||||
};
|
||||
}
|
||||
case 'SET_USER': {
|
||||
return {
|
||||
...state,
|
||||
user: action.user,
|
||||
};
|
||||
}
|
||||
case 'FIND_NOTES': {
|
||||
const notes = state.notes.value
|
||||
.filter((o) => o.title.search(action.qry) > -1)
|
||||
.map((o) => {
|
||||
return {
|
||||
qry: o.id,
|
||||
dict: 'n/a',
|
||||
type: CardType.Note,
|
||||
data: o,
|
||||
};
|
||||
});
|
||||
|
||||
let cards = [] as CardItem[];
|
||||
|
||||
if (action.nextToItem && state.displaySettings.value.insertCardNextToItem) {
|
||||
const idx = state.cards.indexOf(action.nextToItem);
|
||||
|
||||
if (state.displaySettings.value.appendCardToBottom) {
|
||||
const before = state.cards.slice(0, idx + 1);
|
||||
const after = state.cards.slice(idx + 1);
|
||||
cards = [...before, ...notes, ...after];
|
||||
} else {
|
||||
const before = state.cards.slice(0, idx);
|
||||
const after = state.cards.slice(idx);
|
||||
cards = [...before, ...notes, ...after];
|
||||
}
|
||||
} else {
|
||||
if (state.displaySettings.value.appendCardToBottom) {
|
||||
cards = [...state.cards, ...notes];
|
||||
} else {
|
||||
cards = [...notes, ...state.cards];
|
||||
}
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
cards,
|
||||
};
|
||||
}
|
||||
case 'GET_NOTE': {
|
||||
const note = state.notes.value.find((o) => o.id === action.noteId);
|
||||
const card = {
|
||||
qry: note.id,
|
||||
dict: 'n/a',
|
||||
type: CardType.Note,
|
||||
data: note,
|
||||
};
|
||||
|
||||
return reducer(state, {
|
||||
type: 'ADD_CARD',
|
||||
card,
|
||||
nextToItem: action.nextToItem,
|
||||
});
|
||||
}
|
||||
case 'UPDATE_NOTES': {
|
||||
return {
|
||||
...state,
|
||||
notes: action.notes,
|
||||
};
|
||||
}
|
||||
case 'SAVE_NOTE': {
|
||||
// you may be creating a new note or updating an existing.
|
||||
// if its an update, you need to update the note in the following:
|
||||
// * card list could have it.
|
||||
// * notes list could have it.
|
||||
// * it could be in any of the saved pages lists...
|
||||
// so iterate through all of them and if you find the note
|
||||
// in any of them, swap it out
|
||||
|
||||
const cards = [
|
||||
...state.cards.map((o) => {
|
||||
const n = o.data as NoteItem;
|
||||
if (n && n.id === action.note.id) {
|
||||
return {
|
||||
...o,
|
||||
data: action.note,
|
||||
};
|
||||
}
|
||||
return o;
|
||||
}),
|
||||
];
|
||||
|
||||
const notes = new Storable<NoteItem[]>([
|
||||
...state.notes.value.filter((o) => o.id !== action.note.id),
|
||||
action.note,
|
||||
]);
|
||||
|
||||
const savedPages = new Storable<SavedPage[]>([
|
||||
...state.savedPages.value.map((sp) => {
|
||||
return {
|
||||
...sp,
|
||||
queries: sp.queries.map((o) => {
|
||||
const n = o.data as NoteItem;
|
||||
if (n && n.id === action.note.id) {
|
||||
return {
|
||||
...o,
|
||||
data: action.note,
|
||||
};
|
||||
}
|
||||
return o;
|
||||
}),
|
||||
};
|
||||
}),
|
||||
]);
|
||||
const newState = {
|
||||
...state,
|
||||
cards,
|
||||
notes,
|
||||
savedPages,
|
||||
};
|
||||
return newState;
|
||||
}
|
||||
case 'DELETE_NOTE': {
|
||||
// the note may be in any of the following:
|
||||
// * card list could have it.
|
||||
// * notes list could have it.
|
||||
// * it could be in any of the saved pages lists...
|
||||
// so iterate through all of them and if you find the note
|
||||
// in any of them, remove it
|
||||
|
||||
const cards = [
|
||||
...state.cards.filter((o) => {
|
||||
const n = o.data as NoteItem;
|
||||
return !n || n.id !== action.note.id;
|
||||
}),
|
||||
];
|
||||
|
||||
const notes = new Storable<NoteItem[]>([...state.notes.value.filter((o) => o.id !== action.note.id)]);
|
||||
|
||||
const savedPages = new Storable<SavedPage[]>([
|
||||
...state.savedPages.value.map((sp) => {
|
||||
return {
|
||||
...sp,
|
||||
queries: sp.queries.filter((o) => {
|
||||
const n = o.data as NoteItem;
|
||||
return !n || n.id !== action.note.id;
|
||||
}),
|
||||
};
|
||||
}),
|
||||
]);
|
||||
return {
|
||||
...state,
|
||||
cards,
|
||||
notes,
|
||||
savedPages,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
import { reducer } from './app-state-reducer';
|
||||
import { initialState } from './app-state-initial-state';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
|
Loading…
x
Reference in New Issue
Block a user