mirror of
https://gitlab.com/walljm/dynamicbible.git
synced 2025-07-24 16:00:11 -04:00
sync local and remote display settings
This commit is contained in:
parent
573b9dc29d
commit
25ed414aba
@ -10,7 +10,7 @@ trim_trailing_whitespace = true
|
|||||||
|
|
||||||
[*.ts]
|
[*.ts]
|
||||||
quote_type = single
|
quote_type = single
|
||||||
max_line_length = 140
|
max_line_length = 120
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
|
|
||||||
# this file has a need for arbitrarily long lines, so we exempt this here.
|
# this file has a need for arbitrarily long lines, so we exempt this here.
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"targets": {
|
"targets": {
|
||||||
"dynamicbible-7c6cf": {
|
"dynamic-bible-testing": {
|
||||||
"hosting": {
|
"hosting": {
|
||||||
"db": [
|
"db": [
|
||||||
"dynamicbible-7c6cf"
|
"dynamic-bible-testing"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Component, ViewChild, AfterViewInit } from '@angular/core';
|
import { Component, ViewChild, AfterViewInit } from '@angular/core';
|
||||||
import { AppService } from './services/app.service';
|
import { AppService } from './services/app.service';
|
||||||
import { NavService } from './services/nav.service';
|
import { NavService } from './services/nav.service';
|
||||||
|
import { StorageService } from './services/storage.service';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { Breakpoints, BreakpointObserver } from '@angular/cdk/layout';
|
import { Breakpoints, BreakpointObserver } from '@angular/cdk/layout';
|
||||||
import { map, shareReplay } from 'rxjs/operators';
|
import { map, shareReplay } from 'rxjs/operators';
|
||||||
@ -16,20 +17,15 @@ import { SubscriberComponent } from './common/components/subscriber.component';
|
|||||||
styleUrls: ['./app.component.scss'],
|
styleUrls: ['./app.component.scss'],
|
||||||
})
|
})
|
||||||
export class AppComponent extends SubscriberComponent implements AfterViewInit {
|
export class AppComponent extends SubscriberComponent implements AfterViewInit {
|
||||||
savedPages$ = this.appService.select((state) => state.savedPages);
|
savedPages$ = this.appService.select((state) => state.savedPages.value);
|
||||||
mainPages$ = this.appService.select((state) => state.mainPages);
|
mainPages$ = this.appService.select((state) => state.mainPages);
|
||||||
fontSize$ = this.appService.select(
|
fontSize$ = this.appService.select((state) => state.displaySettings.value.cardFontSize + 'pt');
|
||||||
(state) => state.displaySettings.fontSize + 'pt'
|
cardFont$ = this.appService.select((state) => state.displaySettings.value.cardFontFamily);
|
||||||
);
|
|
||||||
cardFont$ = this.appService.select((state) => state.displaySettings.cardFont);
|
|
||||||
displaySettings$ = this.appService.select((state) => state.displaySettings);
|
|
||||||
|
|
||||||
isHandset$: Observable<boolean> = this.breakpointObserver
|
isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset).pipe(
|
||||||
.observe(Breakpoints.Handset)
|
map((result) => result.matches),
|
||||||
.pipe(
|
shareReplay()
|
||||||
map((result) => result.matches),
|
);
|
||||||
shareReplay()
|
|
||||||
);
|
|
||||||
|
|
||||||
@ViewChild('drawer') public sidenav: MatSidenav;
|
@ViewChild('drawer') public sidenav: MatSidenav;
|
||||||
@ViewChild('settings') public settings: MatSidenav;
|
@ViewChild('settings') public settings: MatSidenav;
|
||||||
@ -37,26 +33,28 @@ export class AppComponent extends SubscriberComponent implements AfterViewInit {
|
|||||||
constructor(
|
constructor(
|
||||||
private appService: AppService,
|
private appService: AppService,
|
||||||
private navService: NavService,
|
private navService: NavService,
|
||||||
|
private storageService: StorageService,
|
||||||
private breakpointObserver: BreakpointObserver,
|
private breakpointObserver: BreakpointObserver,
|
||||||
private dialog: MatDialog
|
private dialog: MatDialog
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.appService.initSavedPages();
|
this.storageService.initSavedPages();
|
||||||
this.appService.initDisplaySettings();
|
this.storageService.initDisplaySettings();
|
||||||
|
|
||||||
//#region handle local storage
|
//#region Handle recieving updates from firebase
|
||||||
|
|
||||||
this.addSubscription(
|
this.addSubscription(
|
||||||
this.displaySettings$.subscribe((settings) => {
|
// when the user object changes, respond
|
||||||
this.appService.saveSettingsApi(settings);
|
this.appService
|
||||||
})
|
// this should trigger only once, when the user logs in.
|
||||||
);
|
.select((state) => state.user)
|
||||||
|
.subscribe((user) => {
|
||||||
this.addSubscription(
|
if (!user) {
|
||||||
this.savedPages$.subscribe((savedPages) => {
|
return; // if the user is null, avoid this.
|
||||||
this.appService.savePagesApi(savedPages);
|
}
|
||||||
})
|
this.storageService.initRemote(user);
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
@ -73,10 +71,7 @@ export class AppComponent extends SubscriberComponent implements AfterViewInit {
|
|||||||
this.addSubscription(
|
this.addSubscription(
|
||||||
this.cardFont$.subscribe((family) => {
|
this.cardFont$.subscribe((family) => {
|
||||||
if (family) {
|
if (family) {
|
||||||
document.documentElement.style.setProperty(
|
document.documentElement.style.setProperty('--card-font-family', family);
|
||||||
'--card-font-family',
|
|
||||||
family
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -93,7 +88,7 @@ export class AppComponent extends SubscriberComponent implements AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updatePage() {
|
updatePage() {
|
||||||
this.appService.updatePage();
|
this.appService.updateSavedPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
createNote() {
|
createNote() {
|
||||||
|
@ -1,29 +1,34 @@
|
|||||||
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
|
||||||
import { AppComponent } from './app.component';
|
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
|
||||||
|
|
||||||
import { NgxMdModule } from 'ngx-md';
|
import { NgxMdModule } from 'ngx-md';
|
||||||
|
|
||||||
|
import { AngularFireModule } from '@angular/fire';
|
||||||
|
import { FirebaseConfig } from './constants';
|
||||||
|
import { AngularFireAuthModule } from '@angular/fire/auth';
|
||||||
|
import { AngularFireDatabaseModule } from '@angular/fire/database';
|
||||||
|
|
||||||
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
import { SearchPage } from './search/components/search-page/search.page';
|
import { SearchPage } from './search/components/search-page/search.page';
|
||||||
import { PassageCardComponent } from './search/components/passage/passage-card.component';
|
|
||||||
import { WordsCardComponent } from './search/components/words/words-card.component';
|
|
||||||
import { NoteCardComponent } from './search/components/note/note-card.component';
|
|
||||||
import { SettingsComponent } from './common/components/settings/settings.component';
|
|
||||||
|
|
||||||
import { StrongsComponent } from './search/components/strongs/strongs.component';
|
|
||||||
import { StrongsCardComponent } from './search/components/strongs/card/strongs-card.component';
|
|
||||||
import { StrongsModalComponent } from './search/components/strongs/modal/strongs-modal.component';
|
|
||||||
|
|
||||||
import { AddToPageModalComponent } from './search/components/saved-page/add-to-page-modal/add-to-page-modal.component';
|
import { AddToPageModalComponent } from './search/components/saved-page/add-to-page-modal/add-to-page-modal.component';
|
||||||
import { PageEditModalComponent } from './search/components/saved-page/page-edit-modal/page-edit-modal.component';
|
import { PageEditModalComponent } from './search/components/saved-page/page-edit-modal/page-edit-modal.component';
|
||||||
import { NoteEditModalComponent } from './search/components/note/edit-modal/note-edit-modal.component';
|
import { NoteEditModalComponent } from './search/components/note/edit-modal/note-edit-modal.component';
|
||||||
import { VersePickerModalComponent } from './search/components/verse-picker-modal/verse-picker-modal.component';
|
import { VersePickerModalComponent } from './search/components/verse-picker-modal/verse-picker-modal.component';
|
||||||
|
|
||||||
|
import { SettingsComponent } from './common/components/settings/settings.component';
|
||||||
|
import { PassageCardComponent } from './search/components/passage/passage-card.component';
|
||||||
|
import { WordsCardComponent } from './search/components/words/words-card.component';
|
||||||
|
import { NoteCardComponent } from './search/components/note/note-card.component';
|
||||||
|
|
||||||
|
import { StrongsComponent } from './search/components/strongs/strongs.component';
|
||||||
|
import { StrongsCardComponent } from './search/components/strongs/card/strongs-card.component';
|
||||||
|
import { StrongsModalComponent } from './search/components/strongs/modal/strongs-modal.component';
|
||||||
|
|
||||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
@ -60,8 +65,6 @@ import { MatDividerModule } from '@angular/material/divider';
|
|||||||
import { MatNativeDateModule, MatRippleModule } from '@angular/material/core';
|
import { MatNativeDateModule, MatRippleModule } from '@angular/material/core';
|
||||||
import { MatTreeModule } from '@angular/material/tree';
|
import { MatTreeModule } from '@angular/material/tree';
|
||||||
import { ClipboardModule } from '@angular/cdk/clipboard';
|
import { ClipboardModule } from '@angular/cdk/clipboard';
|
||||||
import { AngularFireModule } from '@angular/fire';
|
|
||||||
import { FirebaseConfig } from './constants';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -91,6 +94,8 @@ import { FirebaseConfig } from './constants';
|
|||||||
NgxMdModule.forRoot(),
|
NgxMdModule.forRoot(),
|
||||||
|
|
||||||
AngularFireModule.initializeApp(FirebaseConfig),
|
AngularFireModule.initializeApp(FirebaseConfig),
|
||||||
|
AngularFireAuthModule,
|
||||||
|
AngularFireDatabaseModule,
|
||||||
|
|
||||||
MatSidenavModule,
|
MatSidenavModule,
|
||||||
MatToolbarModule,
|
MatToolbarModule,
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
import {
|
import { EventEmitter, Output, Input, ElementRef, Component } from '@angular/core';
|
||||||
EventEmitter,
|
|
||||||
Output,
|
|
||||||
Input,
|
|
||||||
ElementRef,
|
|
||||||
Component,
|
|
||||||
} from '@angular/core';
|
|
||||||
import { CardItem, OpenData } from '../../models/app-state';
|
import { CardItem, OpenData } from '../../models/app-state';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
@ -75,7 +75,7 @@
|
|||||||
<div class="settings-h2">Card Font Family</div>
|
<div class="settings-h2">Card Font Family</div>
|
||||||
<mat-form-field appearance="fill" class="card-fonts">
|
<mat-form-field appearance="fill" class="card-fonts">
|
||||||
<mat-select
|
<mat-select
|
||||||
[(value)]="cardFont"
|
[(value)]="cardFontFamily"
|
||||||
(selectionChange)="cardFontSelected($event)"
|
(selectionChange)="cardFontSelected($event)"
|
||||||
>
|
>
|
||||||
<mat-option *ngFor="let font of fonts" [value]="font">
|
<mat-option *ngFor="let font of fonts" [value]="font">
|
||||||
@ -88,9 +88,9 @@
|
|||||||
<div class="settings-h2">Card Font Size</div>
|
<div class="settings-h2">Card Font Size</div>
|
||||||
<mat-slider
|
<mat-slider
|
||||||
class="font-size-slider"
|
class="font-size-slider"
|
||||||
max="32"
|
max="24"
|
||||||
min="10"
|
min="12"
|
||||||
step="1"
|
step="2"
|
||||||
thumbLabel="true"
|
thumbLabel="true"
|
||||||
(change)="cardFontSizeChanged($event)"
|
(change)="cardFontSizeChanged($event)"
|
||||||
[(ngModel)]="cardFontSize"
|
[(ngModel)]="cardFontSize"
|
||||||
|
@ -8,6 +8,7 @@ import { MatSelectChange } from '@angular/material/select';
|
|||||||
import { MatSliderChange } from '@angular/material/slider';
|
import { MatSliderChange } from '@angular/material/slider';
|
||||||
import { AngularFireAuth } from '@angular/fire/auth';
|
import { AngularFireAuth } from '@angular/fire/auth';
|
||||||
import { auth } from 'firebase/app';
|
import { auth } from 'firebase/app';
|
||||||
|
import { Storable } from 'src/app/models/storable';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-settings',
|
selector: 'app-settings',
|
||||||
@ -17,7 +18,7 @@ import { auth } from 'firebase/app';
|
|||||||
export class SettingsComponent extends SubscriberComponent {
|
export class SettingsComponent extends SubscriberComponent {
|
||||||
displaySettings: DisplaySettings;
|
displaySettings: DisplaySettings;
|
||||||
fonts: string[];
|
fonts: string[];
|
||||||
cardFont = '';
|
cardFontFamily = '';
|
||||||
cardFontSize = 10;
|
cardFontSize = 10;
|
||||||
user$ = this.appService.select((state) => state.user);
|
user$ = this.appService.select((state) => state.user);
|
||||||
|
|
||||||
@ -26,25 +27,30 @@ export class SettingsComponent extends SubscriberComponent {
|
|||||||
this.fonts = CardFonts;
|
this.fonts = CardFonts;
|
||||||
this.addSubscription(
|
this.addSubscription(
|
||||||
this.appService.state$.subscribe((state) => {
|
this.appService.state$.subscribe((state) => {
|
||||||
this.displaySettings = state.displaySettings;
|
this.displaySettings = state.displaySettings.value;
|
||||||
this.cardFont = state.displaySettings.cardFont;
|
this.cardFontFamily = state.displaySettings.value.cardFontFamily;
|
||||||
|
this.cardFontSize = state.displaySettings.value.cardFontSize;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.addSubscription(
|
this.addSubscription(
|
||||||
this.authService.authState.subscribe((user) => {
|
this.authService.authState.subscribe((user) => {
|
||||||
this.appService.setUser({
|
if (user) {
|
||||||
uid: user.uid,
|
this.appService.setUser({
|
||||||
displayName: user.displayName,
|
uid: user.uid,
|
||||||
providerId: user.providerId,
|
displayName: user.displayName,
|
||||||
email: user.email,
|
providerId: user.providerId,
|
||||||
});
|
email: user.email,
|
||||||
|
});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
login() {
|
login() {
|
||||||
this.authService.signInWithPopup(new auth.GoogleAuthProvider());
|
this.authService.signInWithPopup(new auth.GoogleAuthProvider()).then((cred) => {
|
||||||
|
console.log('Authenticated.');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
logout() {
|
logout() {
|
||||||
this.authService.signOut();
|
this.authService.signOut();
|
||||||
@ -65,68 +71,86 @@ export class SettingsComponent extends SubscriberComponent {
|
|||||||
//#region Search Settings
|
//#region Search Settings
|
||||||
|
|
||||||
toggleStrongsAsModal(toggle: MatSlideToggleChange) {
|
toggleStrongsAsModal(toggle: MatSlideToggleChange) {
|
||||||
this.appService.updateDisplaySettings({
|
this.appService.updateDisplaySettings(
|
||||||
...this.displaySettings,
|
new Storable({
|
||||||
showStrongsAsModal: toggle.checked,
|
...this.displaySettings,
|
||||||
});
|
showStrongsAsModal: toggle.checked,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleAppendCardToBottom(toggle: MatSlideToggleChange) {
|
toggleAppendCardToBottom(toggle: MatSlideToggleChange) {
|
||||||
this.appService.updateDisplaySettings({
|
this.appService.updateDisplaySettings(
|
||||||
...this.displaySettings,
|
new Storable({
|
||||||
appendCardToBottom: toggle.checked,
|
...this.displaySettings,
|
||||||
});
|
appendCardToBottom: toggle.checked,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleInsertCardNextToItem(toggle: MatSlideToggleChange) {
|
toggleInsertCardNextToItem(toggle: MatSlideToggleChange) {
|
||||||
this.appService.updateDisplaySettings({
|
this.appService.updateDisplaySettings(
|
||||||
...this.displaySettings,
|
new Storable({
|
||||||
insertCardNextToItem: toggle.checked,
|
...this.displaySettings,
|
||||||
});
|
insertCardNextToItem: toggle.checked,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleClearSearchAfterQuery(toggle: MatSlideToggleChange) {
|
toggleClearSearchAfterQuery(toggle: MatSlideToggleChange) {
|
||||||
this.appService.updateDisplaySettings({
|
this.appService.updateDisplaySettings(
|
||||||
...this.displaySettings,
|
new Storable({
|
||||||
clearSearchAfterQuery: toggle.checked,
|
...this.displaySettings,
|
||||||
});
|
clearSearchAfterQuery: toggle.checked,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
toggleSyncCardsAcrossDevices(toggle: MatSlideToggleChange) {
|
toggleSyncCardsAcrossDevices(toggle: MatSlideToggleChange) {
|
||||||
this.appService.updateDisplaySettings({
|
this.appService.updateDisplaySettings(
|
||||||
...this.displaySettings,
|
new Storable({
|
||||||
syncCardsAcrossDevices: toggle.checked,
|
...this.displaySettings,
|
||||||
});
|
syncCardsAcrossDevices: toggle.checked,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region Passage Settings
|
//#region Passage Settings
|
||||||
|
|
||||||
toggleParagraphHeadings(toggle: MatSlideToggleChange) {
|
toggleParagraphHeadings(toggle: MatSlideToggleChange) {
|
||||||
this.appService.updateDisplaySettings({
|
this.appService.updateDisplaySettings(
|
||||||
...this.displaySettings,
|
new Storable({
|
||||||
showParagraphHeadings: toggle.checked,
|
...this.displaySettings,
|
||||||
});
|
showParagraphHeadings: toggle.checked,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleParagraphs(toggle: MatSlideToggleChange) {
|
toggleParagraphs(toggle: MatSlideToggleChange) {
|
||||||
this.appService.updateDisplaySettings({
|
this.appService.updateDisplaySettings(
|
||||||
...this.displaySettings,
|
new Storable({
|
||||||
showParagraphs: toggle.checked,
|
...this.displaySettings,
|
||||||
});
|
showParagraphs: toggle.checked,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleVerseNumbers(toggle: MatSlideToggleChange) {
|
toggleVerseNumbers(toggle: MatSlideToggleChange) {
|
||||||
this.appService.updateDisplaySettings({
|
this.appService.updateDisplaySettings(
|
||||||
...this.displaySettings,
|
new Storable({
|
||||||
showVerseNumbers: toggle.checked,
|
...this.displaySettings,
|
||||||
});
|
showVerseNumbers: toggle.checked,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleVersesOnNewLine(toggle: MatSlideToggleChange) {
|
toggleVersesOnNewLine(toggle: MatSlideToggleChange) {
|
||||||
this.appService.updateDisplaySettings({
|
this.appService.updateDisplaySettings(
|
||||||
...this.displaySettings,
|
new Storable({
|
||||||
showVersesOnNewLine: toggle.checked,
|
...this.displaySettings,
|
||||||
});
|
showVersesOnNewLine: toggle.checked,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
@ -14,13 +14,30 @@ export const CardIcons = {
|
|||||||
Strongs: 'article',
|
Strongs: 'article',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CardFonts = ['Merriweather', 'PT Sans', 'PT Serif', 'Open Sans', 'Roboto', 'Roboto Condensed', 'Inconsolata'];
|
export const CardFonts = [
|
||||||
|
'Merriweather',
|
||||||
|
'PT Sans',
|
||||||
|
'PT Serif',
|
||||||
|
'Open Sans',
|
||||||
|
'Roboto',
|
||||||
|
'Roboto Condensed',
|
||||||
|
'Inconsolata',
|
||||||
|
];
|
||||||
|
|
||||||
|
// export const FirebaseConfig = {
|
||||||
|
// apiKey: 'AIzaSyA3UV4s56CV2EumgvZmyJBTyU-vhv0xhc8',
|
||||||
|
// authDomain: 'dynamicbible-7c6cf.firebaseapp.com',
|
||||||
|
// databaseURL: 'https://dynamicbible-7c6cf.firebaseio.com',
|
||||||
|
// projectId: 'dynamicbible-7c6cf',
|
||||||
|
// storageBucket: '',
|
||||||
|
// messagingSenderId: '200739882604',
|
||||||
|
// };
|
||||||
export const FirebaseConfig = {
|
export const FirebaseConfig = {
|
||||||
apiKey: 'AIzaSyA3UV4s56CV2EumgvZmyJBTyU-vhv0xhc8',
|
apiKey: 'AIzaSyA4b587psiOnpjbzu0t6z75A_hFksPyQkI',
|
||||||
authDomain: 'dynamicbible-7c6cf.firebaseapp.com',
|
authDomain: 'dynamic-bible-testing.firebaseapp.com',
|
||||||
databaseURL: 'https://dynamicbible-7c6cf.firebaseio.com',
|
databaseURL: 'https://dynamic-bible-testing.firebaseio.com',
|
||||||
projectId: 'dynamicbible-7c6cf',
|
projectId: 'dynamic-bible-testing',
|
||||||
storageBucket: '',
|
storageBucket: 'dynamic-bible-testing.appspot.com',
|
||||||
messagingSenderId: '200739882604',
|
messagingSenderId: '813845246474',
|
||||||
|
appId: '1:813845246474:web:6dccfa057b6cb3067565f3',
|
||||||
};
|
};
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
|
import { IStorable } from './storable';
|
||||||
|
|
||||||
export interface AppState {
|
export interface AppState {
|
||||||
readonly currentPage: SavedPage;
|
readonly currentPage: SavedPage;
|
||||||
readonly savedPages: readonly SavedPage[];
|
readonly savedPages: IStorable<readonly SavedPage[]>;
|
||||||
readonly savedPagesLoaded: boolean;
|
readonly savedPagesLoaded: boolean;
|
||||||
readonly mainPages: readonly Page[];
|
readonly mainPages: readonly Page[];
|
||||||
readonly cards: readonly CardItem[];
|
readonly cards: readonly CardItem[];
|
||||||
readonly autocomplete: readonly string[];
|
readonly autocomplete: readonly string[];
|
||||||
readonly error: Error;
|
readonly error: Error;
|
||||||
readonly displaySettings: DisplaySettings;
|
readonly displaySettings: IStorable<DisplaySettings>;
|
||||||
readonly cardIcons: CardIcons;
|
readonly cardIcons: CardIcons;
|
||||||
readonly user: User;
|
readonly user: User;
|
||||||
}
|
}
|
||||||
@ -33,12 +35,14 @@ export interface CardIcons {
|
|||||||
|
|
||||||
export interface DisplaySettings {
|
export interface DisplaySettings {
|
||||||
readonly showStrongsAsModal: boolean;
|
readonly showStrongsAsModal: boolean;
|
||||||
|
|
||||||
readonly appendCardToBottom: boolean;
|
readonly appendCardToBottom: boolean;
|
||||||
readonly insertCardNextToItem: boolean;
|
readonly insertCardNextToItem: boolean;
|
||||||
|
|
||||||
readonly clearSearchAfterQuery: boolean;
|
readonly clearSearchAfterQuery: boolean;
|
||||||
|
|
||||||
readonly fontSize: number;
|
readonly cardFontSize: number;
|
||||||
readonly cardFont: string;
|
readonly cardFontFamily: string;
|
||||||
|
|
||||||
readonly showVersesOnNewLine: boolean;
|
readonly showVersesOnNewLine: boolean;
|
||||||
readonly showVerseNumbers: boolean;
|
readonly showVerseNumbers: boolean;
|
||||||
|
14
app/db/src/app/models/storable.ts
Normal file
14
app/db/src/app/models/storable.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export interface IStorable<T> {
|
||||||
|
readonly createdOn: string;
|
||||||
|
readonly value: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Storable<T> implements IStorable<T> {
|
||||||
|
constructor(v: T) {
|
||||||
|
this.value = v;
|
||||||
|
this.createdOn = new Date().toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
|
createdOn: string;
|
||||||
|
value: T;
|
||||||
|
}
|
@ -15,30 +15,18 @@ import { StrongsModalComponent } from '../strongs/modal/strongs-modal.component'
|
|||||||
export class PassageCardComponent extends CardComponent implements OnInit {
|
export class PassageCardComponent extends CardComponent implements OnInit {
|
||||||
ref: BibleReference;
|
ref: BibleReference;
|
||||||
|
|
||||||
showParagraphs$ = this.appService.select(
|
showParagraphs$ = this.appService.select((state) => state.displaySettings.value.showParagraphs);
|
||||||
(state) => state.displaySettings.showParagraphs
|
|
||||||
);
|
|
||||||
showParagraphHeadings$ = this.appService.select(
|
showParagraphHeadings$ = this.appService.select(
|
||||||
(state) =>
|
(state) => state.displaySettings.value.showParagraphHeadings && state.displaySettings.value.showParagraphs
|
||||||
state.displaySettings.showParagraphHeadings &&
|
|
||||||
state.displaySettings.showParagraphs
|
|
||||||
);
|
|
||||||
showVersesOnNewLine$ = this.appService.select(
|
|
||||||
(state) => state.displaySettings.showVersesOnNewLine
|
|
||||||
);
|
|
||||||
showVerseNumbers$ = this.appService.select(
|
|
||||||
(state) => state.displaySettings.showVerseNumbers
|
|
||||||
);
|
);
|
||||||
|
showVersesOnNewLine$ = this.appService.select((state) => state.displaySettings.value.showVersesOnNewLine);
|
||||||
|
showVerseNumbers$ = this.appService.select((state) => state.displaySettings.value.showVerseNumbers);
|
||||||
|
|
||||||
displaySettings$ = this.appService.select((state) => state.displaySettings);
|
displaySettings$ = this.appService.select((state) => state.displaySettings.value);
|
||||||
|
|
||||||
@ViewChild('passage') passageElement: ElementRef;
|
@ViewChild('passage') passageElement: ElementRef;
|
||||||
|
|
||||||
constructor(
|
constructor(protected elementRef: ElementRef, private appService: AppService, public dialog: MatDialog) {
|
||||||
protected elementRef: ElementRef,
|
|
||||||
private appService: AppService,
|
|
||||||
public dialog: MatDialog
|
|
||||||
) {
|
|
||||||
super(elementRef, dialog);
|
super(elementRef, dialog);
|
||||||
this.icon$ = appService.select((state) => state.cardIcons.passage);
|
this.icon$ = appService.select((state) => state.cardIcons.passage);
|
||||||
}
|
}
|
||||||
@ -54,14 +42,9 @@ export class PassageCardComponent extends CardComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
next() {
|
next() {
|
||||||
const lastVerseForEnd = this.ref.section.book.chapters[
|
const lastVerseForEnd = this.ref.section.book.chapters[this.ref.section.end.chapter];
|
||||||
this.ref.section.end.chapter
|
|
||||||
];
|
|
||||||
|
|
||||||
if (
|
if (this.ref.section.end.verse !== 0 && this.ref.section.end.verse !== lastVerseForEnd) {
|
||||||
this.ref.section.end.verse !== 0 &&
|
|
||||||
this.ref.section.end.verse !== lastVerseForEnd
|
|
||||||
) {
|
|
||||||
this.ref.section.end.chapter = this.ref.section.end.chapter;
|
this.ref.section.end.chapter = this.ref.section.end.chapter;
|
||||||
} else {
|
} else {
|
||||||
this.ref.section.end.chapter = this.ref.section.end.chapter + 1;
|
this.ref.section.end.chapter = this.ref.section.end.chapter + 1;
|
||||||
@ -69,9 +52,7 @@ export class PassageCardComponent extends CardComponent implements OnInit {
|
|||||||
|
|
||||||
this.ref.section.start.chapter = this.ref.section.end.chapter;
|
this.ref.section.start.chapter = this.ref.section.end.chapter;
|
||||||
this.ref.section.start.verse = 1;
|
this.ref.section.start.verse = 1;
|
||||||
this.ref.section.end.verse = this.ref.section.book.chapters[
|
this.ref.section.end.verse = this.ref.section.book.chapters[this.ref.section.end.chapter];
|
||||||
this.ref.section.end.chapter
|
|
||||||
];
|
|
||||||
|
|
||||||
this.appService.updatePassage(this.cardItem, this.ref);
|
this.appService.updatePassage(this.cardItem, this.ref);
|
||||||
}
|
}
|
||||||
@ -85,24 +66,19 @@ export class PassageCardComponent extends CardComponent implements OnInit {
|
|||||||
|
|
||||||
this.ref.section.end.chapter = this.ref.section.start.chapter;
|
this.ref.section.end.chapter = this.ref.section.start.chapter;
|
||||||
this.ref.section.start.verse = 1;
|
this.ref.section.start.verse = 1;
|
||||||
this.ref.section.end.verse = this.ref.section.book.chapters[
|
this.ref.section.end.verse = this.ref.section.book.chapters[this.ref.section.end.chapter];
|
||||||
this.ref.section.end.chapter
|
|
||||||
];
|
|
||||||
|
|
||||||
this.appService.updatePassage(this.cardItem, this.ref);
|
this.appService.updatePassage(this.cardItem, this.ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
expand() {
|
expand() {
|
||||||
const lastVerseForEnd = this.ref.section.book.chapters[
|
const lastVerseForEnd = this.ref.section.book.chapters[this.ref.section.end.chapter];
|
||||||
this.ref.section.end.chapter
|
|
||||||
];
|
|
||||||
|
|
||||||
// if your verse is at the beginning, to go the prev chapter and add 3 verses from that
|
// if your verse is at the beginning, to go the prev chapter and add 3 verses from that
|
||||||
if (this.ref.section.start.verse < 4) {
|
if (this.ref.section.start.verse < 4) {
|
||||||
this.ref.section.start.chapter = this.ref.section.start.chapter - 1;
|
this.ref.section.start.chapter = this.ref.section.start.chapter - 1;
|
||||||
this.ref.section.start.verse =
|
this.ref.section.start.verse =
|
||||||
this.ref.section.book.chapters[this.ref.section.start.chapter] -
|
this.ref.section.book.chapters[this.ref.section.start.chapter] - (3 - this.ref.section.start.verse);
|
||||||
(3 - this.ref.section.start.verse);
|
|
||||||
if (this.ref.section.start.chapter === 0) {
|
if (this.ref.section.start.chapter === 0) {
|
||||||
this.ref.section.start.chapter = 1;
|
this.ref.section.start.chapter = 1;
|
||||||
this.ref.section.start.verse = 1;
|
this.ref.section.start.verse = 1;
|
||||||
@ -113,22 +89,15 @@ export class PassageCardComponent extends CardComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if your verse is at the end, go to the next chapter
|
// if your verse is at the end, go to the next chapter
|
||||||
if (
|
if (this.ref.section.end.verse === 0 || this.ref.section.end.verse + 3 > lastVerseForEnd) {
|
||||||
this.ref.section.end.verse === 0 ||
|
|
||||||
this.ref.section.end.verse + 3 > lastVerseForEnd
|
|
||||||
) {
|
|
||||||
this.ref.section.end.chapter = this.ref.section.end.chapter + 1;
|
this.ref.section.end.chapter = this.ref.section.end.chapter + 1;
|
||||||
if (this.ref.section.end.verse === 0) {
|
if (this.ref.section.end.verse === 0) {
|
||||||
this.ref.section.end.verse = 3;
|
this.ref.section.end.verse = 3;
|
||||||
} else {
|
} else {
|
||||||
this.ref.section.end.verse =
|
this.ref.section.end.verse = this.ref.section.end.verse + 3 - lastVerseForEnd;
|
||||||
this.ref.section.end.verse + 3 - lastVerseForEnd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (this.ref.section.end.chapter === this.ref.section.book.lastChapter + 1) {
|
||||||
this.ref.section.end.chapter ===
|
|
||||||
this.ref.section.book.lastChapter + 1
|
|
||||||
) {
|
|
||||||
this.ref.section.end.chapter = this.ref.section.book.lastChapter;
|
this.ref.section.end.chapter = this.ref.section.book.lastChapter;
|
||||||
this.ref.section.end.verse = lastVerseForEnd;
|
this.ref.section.end.verse = lastVerseForEnd;
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,7 @@ import { BibleReference } from '../../../common/bible-reference';
|
|||||||
import { VersePickerModalComponent } from '../verse-picker-modal/verse-picker-modal.component';
|
import { VersePickerModalComponent } from '../verse-picker-modal/verse-picker-modal.component';
|
||||||
import { SubscriberComponent } from '../../../common/components/subscriber.component';
|
import { SubscriberComponent } from '../../../common/components/subscriber.component';
|
||||||
|
|
||||||
import {
|
import { MatAutocompleteTrigger, MatAutocomplete } from '@angular/material/autocomplete';
|
||||||
MatAutocompleteTrigger,
|
|
||||||
MatAutocomplete,
|
|
||||||
} from '@angular/material/autocomplete';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-search-page',
|
selector: 'app-search-page',
|
||||||
@ -46,8 +43,7 @@ export class SearchPage extends SubscriberComponent implements OnInit {
|
|||||||
this.addSubscription(
|
this.addSubscription(
|
||||||
this.appService.state$.subscribe((state) => {
|
this.appService.state$.subscribe((state) => {
|
||||||
this.savedPagedLoaded = state.savedPagesLoaded;
|
this.savedPagedLoaded = state.savedPagesLoaded;
|
||||||
this.clearSearchAfterQuery =
|
this.clearSearchAfterQuery = state.displaySettings.value.clearSearchAfterQuery;
|
||||||
state.displaySettings.clearSearchAfterQuery;
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -65,9 +61,7 @@ export class SearchPage extends SubscriberComponent implements OnInit {
|
|||||||
this.init();
|
this.init();
|
||||||
// subscribe to autocomplete input control's changes
|
// subscribe to autocomplete input control's changes
|
||||||
this.addSubscription(
|
this.addSubscription(
|
||||||
this.searchControl.valueChanges.subscribe((value: string) =>
|
this.searchControl.valueChanges.subscribe((value: string) => this.appService.getAutoComplete(value.toLowerCase()))
|
||||||
this.appService.getAutoComplete(value.toLowerCase())
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,16 +15,12 @@ export class StrongsCardComponent extends CardComponent {
|
|||||||
asModal = false;
|
asModal = false;
|
||||||
@ViewChild('strongs') strongsElement: ElementRef;
|
@ViewChild('strongs') strongsElement: ElementRef;
|
||||||
|
|
||||||
constructor(
|
constructor(protected elementRef: ElementRef, protected appService: AppService, protected dialog: MatDialog) {
|
||||||
protected elementRef: ElementRef,
|
|
||||||
protected appService: AppService,
|
|
||||||
protected dialog: MatDialog
|
|
||||||
) {
|
|
||||||
super(elementRef, dialog);
|
super(elementRef, dialog);
|
||||||
this.icon$ = appService.select((state) => state.cardIcons.strongs);
|
this.icon$ = appService.select((state) => state.cardIcons.strongs);
|
||||||
this.addSubscription(
|
this.addSubscription(
|
||||||
this.appService.state$.subscribe((state) => {
|
this.appService.state$.subscribe((state) => {
|
||||||
this.asModal = state.displaySettings.showStrongsAsModal;
|
this.asModal = state.displaySettings.value.showStrongsAsModal;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,8 @@ import { PageTitles, PageIcons } from '../constants';
|
|||||||
import { createStateService } from '../common/state-service';
|
import { createStateService } from '../common/state-service';
|
||||||
import { UUID } from 'angular2-uuid';
|
import { UUID } from 'angular2-uuid';
|
||||||
import { StorageMap } from '@ngx-pwa/local-storage';
|
import { StorageMap } from '@ngx-pwa/local-storage';
|
||||||
|
import { AngularFireDatabase } from '@angular/fire/database';
|
||||||
|
import { IStorable, Storable } from '../models/storable';
|
||||||
|
|
||||||
const initialState: AppState = {
|
const initialState: AppState = {
|
||||||
user: null,
|
user: null,
|
||||||
@ -46,7 +48,10 @@ const initialState: AppState = {
|
|||||||
],
|
],
|
||||||
autocomplete: [],
|
autocomplete: [],
|
||||||
currentPage: null,
|
currentPage: null,
|
||||||
savedPages: [],
|
savedPages: {
|
||||||
|
createdOn: new Date(0).toISOString(),
|
||||||
|
value: [],
|
||||||
|
},
|
||||||
savedPagesLoaded: false,
|
savedPagesLoaded: false,
|
||||||
mainPages: [
|
mainPages: [
|
||||||
{ title: PageTitles.Search, icon: PageIcons.Search, route: 'search' },
|
{ title: PageTitles.Search, icon: PageIcons.Search, route: 'search' },
|
||||||
@ -54,17 +59,20 @@ const initialState: AppState = {
|
|||||||
],
|
],
|
||||||
error: null,
|
error: null,
|
||||||
displaySettings: {
|
displaySettings: {
|
||||||
showStrongsAsModal: false,
|
createdOn: new Date(0).toISOString(),
|
||||||
appendCardToBottom: true,
|
value: {
|
||||||
insertCardNextToItem: true,
|
showStrongsAsModal: false,
|
||||||
clearSearchAfterQuery: true,
|
appendCardToBottom: true,
|
||||||
fontSize: 12,
|
insertCardNextToItem: true,
|
||||||
cardFont: 'PT Serif',
|
clearSearchAfterQuery: true,
|
||||||
showVersesOnNewLine: false,
|
cardFontSize: 12,
|
||||||
showVerseNumbers: false,
|
cardFontFamily: 'PT Serif',
|
||||||
showParagraphs: true,
|
showVersesOnNewLine: false,
|
||||||
showParagraphHeadings: true,
|
showVerseNumbers: false,
|
||||||
syncCardsAcrossDevices: false,
|
showParagraphs: true,
|
||||||
|
showParagraphHeadings: true,
|
||||||
|
syncCardsAcrossDevices: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
cardIcons: {
|
cardIcons: {
|
||||||
words: 'font_download',
|
words: 'font_download',
|
||||||
@ -88,7 +96,7 @@ type AppAction =
|
|||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'UPDATE_SAVED_PAGES';
|
type: 'UPDATE_SAVED_PAGES';
|
||||||
savedPages: SavedPage[];
|
savedPages: IStorable<readonly SavedPage[]>;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'ADD_CARD_TO_SAVED_PAGE';
|
type: 'ADD_CARD_TO_SAVED_PAGE';
|
||||||
@ -115,11 +123,11 @@ type AppAction =
|
|||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'UPDATE_FONT_SIZE';
|
type: 'UPDATE_FONT_SIZE';
|
||||||
size: number;
|
cardFontSize: number;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'UPDATE_FONT_FAMILY';
|
type: 'UPDATE_FONT_FAMILY';
|
||||||
cardFont: string;
|
cardFontFamily: string;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'UPDATE_AUTOCOMPLETE';
|
type: 'UPDATE_AUTOCOMPLETE';
|
||||||
@ -127,13 +135,28 @@ type AppAction =
|
|||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'UPDATE_DISPLAY_SETTINGS';
|
type: 'UPDATE_DISPLAY_SETTINGS';
|
||||||
settings: DisplaySettings;
|
settings: IStorable<DisplaySettings>;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'SET_USER';
|
type: 'SET_USER';
|
||||||
user: User;
|
user: User;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 {
|
function reducer(state: AppState, action: AppAction): AppState {
|
||||||
// somtimes the state is null. lets not complain if that happens.
|
// somtimes the state is null. lets not complain if that happens.
|
||||||
if (state === undefined) {
|
if (state === undefined) {
|
||||||
@ -141,12 +164,41 @@ function reducer(state: AppState, action: AppAction): AppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
//#region Display Settings
|
||||||
|
|
||||||
case 'UPDATE_DISPLAY_SETTINGS': {
|
case 'UPDATE_DISPLAY_SETTINGS': {
|
||||||
return {
|
return maybeMutateStorable(state, action.settings, state.displaySettings, (item) => {
|
||||||
...state,
|
return {
|
||||||
displaySettings: action.settings,
|
...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': {
|
case 'UPDATE_AUTOCOMPLETE': {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@ -154,49 +206,50 @@ function reducer(state: AppState, action: AppAction): AppState {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'UPDATE_SAVED_PAGES': {
|
case 'UPDATE_SAVED_PAGES': {
|
||||||
return {
|
return maybeMutateStorable(state, action.savedPages, state.savedPages, (item) => {
|
||||||
...state,
|
return {
|
||||||
savedPagesLoaded: true,
|
...state,
|
||||||
savedPages: action.savedPages,
|
savedPagesLoaded: true,
|
||||||
};
|
savedPages: item,
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
case 'UPDATE_CURRENT_PAGE': {
|
case 'UPDATE_CURRENT_PAGE': {
|
||||||
const savedPages = [
|
const savedPages = new Storable<SavedPage[]>([
|
||||||
...state.savedPages.filter((o) => o.id === state.currentPage.id),
|
...state.savedPages.value.filter((o) => o.id === state.currentPage.id),
|
||||||
{
|
{
|
||||||
id: state.currentPage.id,
|
id: state.currentPage.id,
|
||||||
title: state.currentPage.title,
|
title: state.currentPage.title,
|
||||||
queries: [...state.cards],
|
queries: [...state.cards],
|
||||||
},
|
},
|
||||||
];
|
]);
|
||||||
|
|
||||||
return {
|
return reducer(state, {
|
||||||
...state,
|
type: 'UPDATE_SAVED_PAGES',
|
||||||
savedPagesLoaded: true,
|
|
||||||
savedPages,
|
savedPages,
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
case 'SAVE_PAGE': {
|
case 'SAVE_PAGE': {
|
||||||
const savedPages = [
|
const savedPages = new Storable([
|
||||||
...state.savedPages,
|
...state.savedPages.value,
|
||||||
{
|
{
|
||||||
// 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: [...state.cards],
|
queries: [...state.cards],
|
||||||
},
|
},
|
||||||
];
|
]);
|
||||||
return {
|
|
||||||
...state,
|
return reducer(state, {
|
||||||
savedPagesLoaded: true,
|
type: 'UPDATE_SAVED_PAGES',
|
||||||
savedPages,
|
savedPages,
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
case 'GET_SAVED_PAGE': {
|
case 'GET_SAVED_PAGE': {
|
||||||
const page = state.savedPages.find((o) => o.id.toString() === action.pageId);
|
const page = state.savedPages.value.find((o) => o.id.toString() === action.pageId);
|
||||||
|
|
||||||
if (!page) {
|
if (!page) {
|
||||||
return;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -206,34 +259,36 @@ function reducer(state: AppState, action: AppAction): AppState {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'ADD_CARD_TO_SAVED_PAGE': {
|
case 'ADD_CARD_TO_SAVED_PAGE': {
|
||||||
return {
|
const savedPages = new Storable([
|
||||||
...state,
|
...state.savedPages.value.map((o) => {
|
||||||
savedPages: [
|
if (o.id.toString() === action.pageId) {
|
||||||
...state.savedPages.map((o) => {
|
let cards = [];
|
||||||
if (o.id.toString() === action.pageId) {
|
if (state.displaySettings.value.appendCardToBottom) {
|
||||||
let cards = [];
|
cards = [...o.queries, action.card];
|
||||||
if (state.displaySettings.appendCardToBottom) {
|
} else {
|
||||||
cards = [...o.queries, action.card];
|
cards = [action.card, ...o.queries];
|
||||||
} else {
|
|
||||||
cards = [action.card, ...o.queries];
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...o,
|
|
||||||
queries: cards,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return o;
|
return {
|
||||||
}),
|
...o,
|
||||||
],
|
queries: cards,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return reducer(state, {
|
||||||
|
type: 'UPDATE_SAVED_PAGES',
|
||||||
|
savedPages,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
case 'ADD_CARD': {
|
case 'ADD_CARD': {
|
||||||
let cards = [];
|
let cards = [];
|
||||||
|
|
||||||
if (action.nextToItem && state.displaySettings.insertCardNextToItem) {
|
if (action.nextToItem && state.displaySettings.value.insertCardNextToItem) {
|
||||||
const idx = state.cards.indexOf(action.nextToItem);
|
const idx = state.cards.indexOf(action.nextToItem);
|
||||||
|
|
||||||
if (state.displaySettings.appendCardToBottom) {
|
if (state.displaySettings.value.appendCardToBottom) {
|
||||||
const before = state.cards.slice(0, idx + 1);
|
const before = state.cards.slice(0, idx + 1);
|
||||||
const after = state.cards.slice(idx + 1);
|
const after = state.cards.slice(idx + 1);
|
||||||
cards = [...before, action.card, ...after];
|
cards = [...before, action.card, ...after];
|
||||||
@ -243,7 +298,7 @@ function reducer(state: AppState, action: AppAction): AppState {
|
|||||||
cards = [...before, action.card, ...after];
|
cards = [...before, action.card, ...after];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (state.displaySettings.appendCardToBottom) {
|
if (state.displaySettings.value.appendCardToBottom) {
|
||||||
cards = [...state.cards, action.card];
|
cards = [...state.cards, action.card];
|
||||||
} else {
|
} else {
|
||||||
cards = [action.card, ...state.cards];
|
cards = [action.card, ...state.cards];
|
||||||
@ -271,24 +326,6 @@ function reducer(state: AppState, action: AppAction): AppState {
|
|||||||
cards: [...state.cards.filter((c) => c !== action.card)],
|
cards: [...state.cards.filter((c) => c !== action.card)],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'UPDATE_FONT_SIZE': {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
displaySettings: {
|
|
||||||
...state.displaySettings,
|
|
||||||
fontSize: action.size,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
case 'UPDATE_FONT_FAMILY': {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
displaySettings: {
|
|
||||||
...state.displaySettings,
|
|
||||||
cardFont: action.cardFont,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
case 'SET_USER': {
|
case 'SET_USER': {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@ -309,7 +346,7 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
|
|
||||||
private readonly dataPath = 'assets/data';
|
private readonly dataPath = 'assets/data';
|
||||||
|
|
||||||
constructor(private http: HttpClient, private localStorageService: StorageMap) {
|
constructor(private http: HttpClient, private localStorageService: StorageMap, private db: AngularFireDatabase) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.searchIndexArray = this.buildIndexArray().sort();
|
this.searchIndexArray = this.buildIndexArray().sort();
|
||||||
@ -322,7 +359,7 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private dispatchError(msg: string) {
|
dispatchError(msg: string) {
|
||||||
this.dispatch({
|
this.dispatch({
|
||||||
type: 'UPDATE_ERROR',
|
type: 'UPDATE_ERROR',
|
||||||
error: {
|
error: {
|
||||||
@ -341,19 +378,6 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
|
|
||||||
//#region Saved Pages
|
//#region Saved Pages
|
||||||
|
|
||||||
async initSavedPages() {
|
|
||||||
const exists = await this.localStorageService.has('savedPages').toPromise();
|
|
||||||
|
|
||||||
if (exists) {
|
|
||||||
const savedPages = (await this.localStorageService.get('savedPages').toPromise()) as SavedPage[];
|
|
||||||
|
|
||||||
this.dispatch({
|
|
||||||
type: 'UPDATE_SAVED_PAGES',
|
|
||||||
savedPages,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getSavedPage(pageid: string) {
|
getSavedPage(pageid: string) {
|
||||||
this.dispatch({
|
this.dispatch({
|
||||||
type: 'GET_SAVED_PAGE',
|
type: 'GET_SAVED_PAGE',
|
||||||
@ -368,30 +392,19 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
savePagesApi(savedPages: readonly SavedPage[]) {
|
updateSavedPage() {
|
||||||
this.localStorageService.set('savedPages', savedPages).subscribe(
|
|
||||||
() => {
|
|
||||||
// nop
|
|
||||||
},
|
|
||||||
// error
|
|
||||||
() => {
|
|
||||||
this.dispatch({
|
|
||||||
type: 'UPDATE_ERROR',
|
|
||||||
error: {
|
|
||||||
// tslint:disable-next-line: quotemark
|
|
||||||
msg: "Something went wrong and the page wasn't saved. :(",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePage() {
|
|
||||||
this.dispatch({
|
this.dispatch({
|
||||||
type: 'UPDATE_CURRENT_PAGE',
|
type: 'UPDATE_CURRENT_PAGE',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateSavedPages(savedPages: IStorable<readonly SavedPage[]>) {
|
||||||
|
this.dispatch({
|
||||||
|
type: 'UPDATE_SAVED_PAGES',
|
||||||
|
savedPages,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
addCardToSavedPage(pageId: string, card: CardItem) {
|
addCardToSavedPage(pageId: string, card: CardItem) {
|
||||||
this.dispatch({
|
this.dispatch({
|
||||||
type: 'ADD_CARD_TO_SAVED_PAGE',
|
type: 'ADD_CARD_TO_SAVED_PAGE',
|
||||||
@ -407,45 +420,24 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
changeCardFontFamily(cardFont: string) {
|
changeCardFontFamily(cardFont: string) {
|
||||||
this.dispatch({
|
this.dispatch({
|
||||||
type: 'UPDATE_FONT_FAMILY',
|
type: 'UPDATE_FONT_FAMILY',
|
||||||
cardFont,
|
cardFontFamily: cardFont,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
changeCardFontSize(size: number) {
|
changeCardFontSize(size: number) {
|
||||||
this.dispatch({
|
this.dispatch({
|
||||||
type: 'UPDATE_FONT_SIZE',
|
type: 'UPDATE_FONT_SIZE',
|
||||||
size,
|
cardFontSize: size,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDisplaySettings(settings: DisplaySettings) {
|
updateDisplaySettings(settings: IStorable<DisplaySettings>) {
|
||||||
this.dispatch({
|
this.dispatch({
|
||||||
type: 'UPDATE_DISPLAY_SETTINGS',
|
type: 'UPDATE_DISPLAY_SETTINGS',
|
||||||
settings,
|
settings,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async initDisplaySettings() {
|
|
||||||
const hasDisplaySettings = await this.localStorageService.has('displaySettings').toPromise();
|
|
||||||
|
|
||||||
if (hasDisplaySettings) {
|
|
||||||
const settings = await this.getSettingsApi();
|
|
||||||
|
|
||||||
this.dispatch({
|
|
||||||
type: 'UPDATE_DISPLAY_SETTINGS',
|
|
||||||
settings,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
saveSettingsApi(settings: DisplaySettings) {
|
|
||||||
return this.localStorageService.set('displaySettings', settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getSettingsApi() {
|
|
||||||
return (await this.localStorageService.get('displaySettings').toPromise()) as DisplaySettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region Notes
|
//#region Notes
|
||||||
@ -581,13 +573,17 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
if (dict === 'grk') {
|
if (dict === 'grk') {
|
||||||
result.prefix = 'G';
|
result.prefix = 'G';
|
||||||
if (sn > 5624 || sn < 1) {
|
if (sn > 5624 || sn < 1) {
|
||||||
this.dispatchError(`Strong's Number G${sn} is out of range. Strong's numbers range from 1 - 5624 in the New Testament.`);
|
this.dispatchError(
|
||||||
|
`Strong's Number G${sn} is out of range. Strong's numbers range from 1 - 5624 in the New Testament.`
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result.prefix = 'H';
|
result.prefix = 'H';
|
||||||
if (sn > 8674 || sn < 1) {
|
if (sn > 8674 || sn < 1) {
|
||||||
this.dispatchError(`Strong's Number H${sn} is out of range. Strong's numbers range from 1 - 8674 in the Old Testament.`);
|
this.dispatchError(
|
||||||
|
`Strong's Number H${sn} is out of range. Strong's numbers range from 1 - 8674 in the Old Testament.`
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -788,7 +784,11 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
return passages;
|
return passages;
|
||||||
}
|
}
|
||||||
|
|
||||||
private convertToParagraphs(ch: BiblePassage, section: Section, paragraphMarkers: HashTable<Paragraph>): BibleParagraph[] {
|
private convertToParagraphs(
|
||||||
|
ch: BiblePassage,
|
||||||
|
section: Section,
|
||||||
|
paragraphMarkers: HashTable<Paragraph>
|
||||||
|
): BibleParagraph[] {
|
||||||
// group the verses into paragraphs.
|
// group the verses into paragraphs.
|
||||||
|
|
||||||
// create an initial paragraph to hold verses that might come before a paragraph.
|
// create an initial paragraph to hold verses that might come before a paragraph.
|
||||||
@ -875,7 +875,9 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
|
|
||||||
// handle the first case.
|
// handle the first case.
|
||||||
if (stem <= this.searchIndexArray[0]) {
|
if (stem <= this.searchIndexArray[0]) {
|
||||||
results.unshift(await this.getSearchReferences(`${this.dataPath}/index/${this.searchIndexArray[0]}idx.json`, stem));
|
results.unshift(
|
||||||
|
await this.getSearchReferences(`${this.dataPath}/index/${this.searchIndexArray[0]}idx.json`, stem)
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -883,7 +885,9 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
for (let w = 1; w < this.searchIndexArray.length; w++) {
|
for (let w = 1; w < this.searchIndexArray.length; w++) {
|
||||||
// If we are at the end of the array, we want to use a different test.
|
// If we are at the end of the array, we want to use a different test.
|
||||||
if (stem <= this.searchIndexArray[w] && stem > this.searchIndexArray[w - 1]) {
|
if (stem <= this.searchIndexArray[w] && stem > this.searchIndexArray[w - 1]) {
|
||||||
results.unshift(await this.getSearchReferences(`${this.dataPath}/index/${this.searchIndexArray[w]}idx.json`, stem));
|
results.unshift(
|
||||||
|
await this.getSearchReferences(`${this.dataPath}/index/${this.searchIndexArray[w]}idx.json`, stem)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // End of loop through query terms
|
} // End of loop through query terms
|
||||||
@ -1290,7 +1294,8 @@ export class AppService extends createStateService(reducer, initialState) {
|
|||||||
for (const item of BibleReference.Books) {
|
for (const item of BibleReference.Books) {
|
||||||
if (
|
if (
|
||||||
item.name !== 'Unknown' &&
|
item.name !== 'Unknown' &&
|
||||||
(item.name.toLowerCase().indexOf(qry.toLowerCase()) > -1 || item.abbreviation.toLowerCase().indexOf(qry.toLowerCase()) > -1)
|
(item.name.toLowerCase().indexOf(qry.toLowerCase()) > -1 ||
|
||||||
|
item.abbreviation.toLowerCase().indexOf(qry.toLowerCase()) > -1)
|
||||||
) {
|
) {
|
||||||
words.push(prefix + item.name);
|
words.push(prefix + item.name);
|
||||||
if (words.length > 2) {
|
if (words.length > 2) {
|
||||||
|
131
app/db/src/app/services/storage.service.ts
Normal file
131
app/db/src/app/services/storage.service.ts
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { StorageMap } from '@ngx-pwa/local-storage';
|
||||||
|
import { AngularFireDatabase, AngularFireObject } from '@angular/fire/database';
|
||||||
|
import { IStorable } from '../models/storable';
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
import { DisplaySettings, SavedPage, User } from '../models/app-state';
|
||||||
|
import { SubscriberComponent } from '../common/components/subscriber.component';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class StorageService extends SubscriberComponent {
|
||||||
|
private displaySettingsState$ = this.appService.select((state) => state.displaySettings);
|
||||||
|
private displaySettingsPath = 'displaySettings';
|
||||||
|
private displaySettingsRemoteObject: AngularFireObject<IStorable<DisplaySettings>>;
|
||||||
|
|
||||||
|
private savedPagesState$ = this.appService.select((state) => state.savedPages);
|
||||||
|
private savedPagesPath = 'savedPaged';
|
||||||
|
private savedPagesRemoteObject: AngularFireObject<IStorable<readonly SavedPage[]>>;
|
||||||
|
|
||||||
|
constructor(private local: StorageMap, private remote: AngularFireDatabase, private appService: AppService) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
//#region 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.displaySettingsState$.subscribe((settings) => {
|
||||||
|
if (!settings) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update local
|
||||||
|
this.local.set('displaySettings', settings).subscribe(
|
||||||
|
() => {
|
||||||
|
// nop
|
||||||
|
},
|
||||||
|
// error
|
||||||
|
() => {
|
||||||
|
// tslint:disable-next-line: quotemark
|
||||||
|
this.appService.dispatchError("Something went wrong and the display settings weren't saved. :(");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// update remote
|
||||||
|
if (this.displaySettingsRemoteObject) {
|
||||||
|
this.displaySettingsRemoteObject.set(settings);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this.addSubscription(
|
||||||
|
this.savedPagesState$.subscribe((savedPages) => {
|
||||||
|
if (!savedPages) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update local
|
||||||
|
this.local.set('savedPages', 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);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
initRemote(user: User) {
|
||||||
|
this.displaySettingsRemoteObject = this.remote.object<IStorable<DisplaySettings>>(
|
||||||
|
`/${this.displaySettingsPath}/${user.uid}`
|
||||||
|
);
|
||||||
|
this.savedPagesRemoteObject = this.remote.object<IStorable<SavedPage[]>>(`/${this.savedPagesPath}/${user.uid}`);
|
||||||
|
|
||||||
|
// display settings
|
||||||
|
this.addSubscription(
|
||||||
|
this.displaySettingsRemoteObject
|
||||||
|
.valueChanges() // when the value changes
|
||||||
|
.subscribe((remoteDisplaySettings) => {
|
||||||
|
if (remoteDisplaySettings) {
|
||||||
|
// update the display settings locally from remote if it isn't null
|
||||||
|
this.appService.updateDisplaySettings(remoteDisplaySettings);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async initDisplaySettings() {
|
||||||
|
const hasDisplaySettings = await this.local.has(this.displaySettingsPath).toPromise();
|
||||||
|
|
||||||
|
if (hasDisplaySettings) {
|
||||||
|
const settings = (await this.local.get(this.displaySettingsPath).toPromise()) as IStorable<DisplaySettings>;
|
||||||
|
|
||||||
|
this.appService.updateDisplaySettings(settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async initSavedPages() {
|
||||||
|
const exists = await this.local.has(this.savedPagesPath).toPromise();
|
||||||
|
|
||||||
|
if (exists) {
|
||||||
|
const savedPages = (await this.local.get(this.savedPagesPath).toPromise()) as IStorable<SavedPage[]>;
|
||||||
|
|
||||||
|
this.appService.updateSavedPages(savedPages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user