FEATURE: Move Help and Settings pages to modals

* fixed ton of bugs... apparently i didn't push up the work i did on the mac... and i don't have that anymore.
This commit is contained in:
walljm 2019-01-02 15:18:49 -05:00
parent 5e8d96f716
commit e444a5fddf
38 changed files with 774 additions and 826 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,17 +1,18 @@
import { Component, ViewChild } from '@angular/core';
import { Platform, MenuController, Nav } from 'ionic-angular';
import { Platform, MenuController, Nav, ModalController } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { SearchPage } from '../pages/search/search';
import { PagesService } from '../services/pages-service';
import { PagesService, Page } from '../services/pages-service';
import { PageTitles } from '../libs/Constants';
@Component({
templateUrl: 'app.html',
providers: [PagesService]
})
export class MyApp
{
export class MyApp {
@ViewChild(Nav) nav: Nav;
rootPage: any = SearchPage;
@ -19,32 +20,39 @@ export class MyApp
constructor(
public platform: Platform,
public menu: MenuController,
private statusBar: StatusBar,
private splash: SplashScreen,
private pagesSvc: PagesService
)
{
public pagesSvc: PagesService,
private _statusBar: StatusBar,
private _splash: SplashScreen,
private _modalCtrl: ModalController
) {
this.initializeApp();
}
initializeApp()
{
this.platform.ready().then(() =>
{
initializeApp() {
this.platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
this.statusBar.styleDefault();
this.splash.hide();
this._statusBar.styleDefault();
this._splash.hide();
});
}
openPage(page)
{
openPage(page: Page) {
if (page.title === PageTitles.Help || page.title === PageTitles.Settings)
{
this.menu.close(); // close the menu
// this is a modal.
const modal = this._modalCtrl.create(page.component);
modal.present();
return;
}
// close the menu when clicking a link from the menu
this.menu.close('pages');
// because the actions menu is on a Page component, and you swap the SearchPage out,
// the menu get registered multiple times. to avoid some pages not opening the menu because
// the menu gets registered multiple times. to avoid some pages not opening the menu because
// multiple menus with the same id exist and the first one in the list is returned (which happens
// be disabled when another of the same id is added) it won't show. because not enabled.
// ---

View File

@ -1,7 +1,7 @@
import { ProfileService } from './../services/profile-service';
import { NgModule, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { HttpClientModule } from '@angular/common/http';
import { HttpModule } from '@angular/http';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
@ -13,17 +13,17 @@ import { SplashScreen } from '@ionic-native/splash-screen';
import { MyApp } from './app.component';
import { SearchPage } from '../pages/search/search';
import { SettingsPage } from '../pages/settings/settings';
import { HelpPage } from '../pages/help/help';
import { ComponentLoader } from '../components/component-loader/component-loader';
import { Passage } from '../components/passage/passage';
import { Strongs } from '../components/strongs/strongs';
import { Words } from '../components/words/words';
import { Error } from '../components/error/error';
import { Settings } from '../components/settings/settings';
import { StrongsModal } from '../components/strongs-modal/strongs-modal';
import { ErrorMessage } from '../components/error-message/error-message';
import { VersePickerModal } from '../components/verse-picker/verse-picker';
import { AboutModal } from '../components/about-modal/about-modal';
import { SettingsModal } from '../components/settings-modal/settings-modal';
import { AngularFireModule } from 'angularfire2';
import { AngularFireAuthModule } from 'angularfire2/auth';
@ -46,9 +46,9 @@ export const firebaseConfig = {
declarations: [
MyApp,
SearchPage,
SettingsPage,
HelpPage,
ComponentLoader,
AboutModal,
SettingsModal,
Settings,
Passage,
Strongs,
StrongsModal,
@ -72,8 +72,9 @@ export const firebaseConfig = {
entryComponents: [
MyApp,
SearchPage,
SettingsPage,
HelpPage,
AboutModal,
SettingsModal,
Settings,
Passage,
Strongs,
StrongsModal,

View File

@ -0,0 +1,122 @@
<ion-header>
<ion-toolbar>
<ion-title>
<ion-icon name="help-circle" item-left></ion-icon> Dynamic Bible Help
</ion-title>
<ion-buttons start>
<button ion-button (click)="dismiss()" large>
<ion-icon name="md-close"></ion-icon>
</button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content padding>
<h2>How to search for a verse</h2>
<p>
To bring up a passage, just type in a reference, or use the verse picker in the top right corner. Dynamic Bible will recognize most abbreviations of books, and can handle ranges within a book. Here are a few examples to get you started:
</p>
<h3>Examples of Search Items:</h3>
<ul>
<li><b>John 1</b> (displays whole chapter)</li>
<li><b>John 3:16</b> (displays single verse)</li>
<li><b>Jn 3:16</b> (handles abbreviation of John)</li>
<li><b>Jn 3:16-17</b> (displays verses starting with chapter 1 verse 3 through chapter 1 vs 5)</li>
<li><b>John 3:16-4:4</b></li>
<li><b>Jn 3-4</b> (displays both chapters)</li>
<li><b>John 3 - John 4</b> (displays both chapters)</li>
<li><b>Jn 3:1-*</b> (this will get all the verses in the chapter. the * char can be used in verse ranges, but not chapter ranges.)</li>
<li><b>H1234</b> (displays the strongs definition for the Hebrew # 1234)</li>
<li><b>G1234</b> (displays the strongs definition for the Greek # 1234)</li>
<li><b>Jesus</b> (searches for "Jesus". all search terms assume boolean AND, i.e. "Jesus Christ" assumes "Jesus AND Christ". A word without a number is interpreted as search term).</li>
</ul>
We currently don't support ranges that cross book boundaries. If you search for "John 12 - Romans 3" you will get an error. the second book name is ignored.
<h3>Further Notes:</h3>
<ul>
<li>Multiple lookups can be made using a semicolon as a seperator, i.e. "Ruth 1; g1234; spirit").</li>
<li>All greek/hebrew cross references translations are taken from the 1933 Websters version and are sometimes not the same as the KJV translation.</li>
<li>All source materials were taken from the <a href="http://sourceforge.net/projects/zefania-sharp/">Zefania XML Bible Project</a> on SourceForge.</li>
<li>Any Errors and Omission you find would be appreciated. Please contact me via <a href='http://www.jasonwall.org/'>www.jasonwall.org</a>.</li>
</ul>
<h3>Visit Us Online</h3>
<p>
<a href="http://www.dynamicbible.com">www.dynamicbible.com</a> hosts the online version of the web app. We are currently available on the
<a href="https://bitbucket.org/walljm/dynamicbible/downloads/DynamicBible-3.1.0.zip">Windows Platform</a> via the <a href="http://electron.atom.io/">Electron app from atom.io</a>
and on the <a href="https://play.google.com/store/apps/details?id=walljm.dynamicbible">Google Android Play store</a>.
We are working on an IOS distribution and will be releasing that soon.
</p>
<h3>Book Names and Accepted Abbreviations</h3>
<ul>
<li><b>Genesis</b>: gen, ge, gn</li>
<li><b>Exodus</b>: ex, exo, exod, exd</li>
<li><b>Leviticus</b>: lev, le, levi, lv</li>
<li><b>Numbers</b>: num, nu, numb, number</li>
<li><b>Deuteronomy</b>: deut, de, dt, deu</li>
<li><b>Joshua</b>: josh, jos</li>
<li><b>Judges</b>: jud, jdg, judg</li>
<li><b>Ruth</b>: ru</li>
<li><b>1 Samuel</b>: 1, i, 1st, first samuel, sa, sam, sml</li>
<li><b>2 Samuel</b>: 2, ii, 2nd, second, sec samuel, sa, sam, sml</li>
<li><b>1 Kings</b>: 1, i, 1st, first kings, king, kgs, kn, k, ki</li>
<li><b>2 Kings</b>: 2, ii, 2nd, second, sec kings, king, kgs, kn, k, ki</li>
<li><b>1 Chronicles</b>: 1, i, 1st, first chronicles, chron, ch, chr</li>
<li><b>2 Chronicles</b>: 2, ii, 2nd, second, sec chronicles, chron, ch, chr</li>
<li><b>Ezra</b>: ezr</li>
<li><b>Nehemiah</b>: neh, ne, nehamiah</li>
<li><b>Esther</b>: est, es, esth</li>
<li><b>Job</b>: jo, jb</li>
<li><b>Psalms</b>: ps, psa, psalm, psm</li>
<li><b>Proverbs</b>: prov, pr, pro, proverb, prv, prvbs</li>
<li><b>Ecclesiastes</b>: eccl, ecc, eccles, ec, ecl, ecclesiaste</li>
<li><b>Song of Solomon</b>: , song of songs, sos, ss, son, so, song, songs</li>
<li><b>Isaiah</b>: is, isah, isai, ia</li>
<li><b>Jerimiah</b>: jeremiah, jer, je, jere</li>
<li><b>Lamentations</b>: lam, la, lamentation</li>
<li><b>Ezekiel</b>: eze, ezk, ezek</li>
<li><b>Daniel</b>: dan, dn, dl, da</li>
<li><b>Hosea</b>: hos, ho</li>
<li><b>Joel</b>: joe, jl</li>
<li><b>Amos</b>: am, amo</li>
<li><b>Obadiah</b>: oba, ob, obad</li>
<li><b>Jonah</b>: jnh, jon</li>
<li><b>Micah</b>: mic, mi</li>
<li><b>Nahum</b>: nah, na</li>
<li><b>Habakkuk</b>: hab, ha, habakuk</li>
<li><b>Zephaniah</b>: zeph, zep</li>
<li><b>Haggia</b>: hag, hg, haggai</li>
<li><b>Zechariah</b>: zech, zch, zec</li>
<li><b>Malachi</b>: mal</li>
<li><b>Matthew</b>: mt, matt, mat</li>
<li><b>Mark</b>: mrk, mk, mr</li>
<li><b>luke</b>: lu, lke, luk, lk</li>
<li><b>John</b>: jn, jhn</li>
<li><b>Acts</b>: ac, act</li>
<li><b>Romans</b>: rom, ro, rm, roman</li>
<li><b>1 Corinthians</b>: 1, i, 1st, first corinthian, cor, corinthians, corinth, corin, corth, corint</li>
<li><b>2 Corinthians</b>: 2, ii, 2nd, second, sec corinthian, cor, corinthians, corinth, corin, corth, corint</li>
<li><b>Galatians</b>: galatian, galations, gal, ga, gala, galation, galat</li>
<li><b>Ephesians</b>: eph, ep, ephes, ephe, ephs</li>
<li><b>Philippians</b>: phi, phil, ph, philip</li>
<li><b>Colossians</b>: col, co, colossian, colos, coloss</li>
<li><b>1 Thessalonians</b>: 1, i, 1st, first thessalonians, the, thessa, thessalonian, thes, thess, th</li>
<li><b>2 Thessalonians</b>: 2, ii, 2nd, second, sec thessalonians, the, thessa, thessalonian, thes, thess, th</li>
<li><b>1 Timothy</b>: 1, i, 1st, first timothy, tim, ti, timoth, tm</li>
<li><b>2 Timothy</b>: 2, ii, 2nd, second, sec timothy, tim, timoth, tm</li>
<li><b>Titus</b>: tit</li>
<li><b>Philemon</b>: phlmn, phl, phm, phile, philem</li>
<li><b>Hebrews</b>: heb, he, hebrew</li>
<li><b>James</b>: jam, ja, jas, jms, jame, jm</li>
<li><b>1 Peter</b>: 1, i, 1st, first peter, pe, pet, pete, pt, p</li>
<li><b>2 Peter</b>: 2, ii, 2nd, second, sec peter, pe, pet, pete, pt, p</li>
<li><b>1 John</b>: 1, i, 1st, first john, jn, jo</li>
<li><b>2 John</b>: 2, ii, 2nd, second, sec john, jn, jo</li>
<li><b>3 John</b>: 3, iii, 3rd, third john, jn, jo</li>
<li><b>Jude</b>: ju</li>
<li><b>Revelation</b>: rev, re, revelations, rv</li>
</ul>
</ion-content>

View File

@ -0,0 +1,9 @@
about-modal {
ion-icon {
float: left;
}
ion-title {
text-align: center;
}
}

View File

@ -0,0 +1,14 @@
import { Component } from '@angular/core';
import { ViewController } from 'ionic-angular';
@Component({
selector: 'about-modal',
templateUrl: 'about-modal.html'
})
export class AboutModal {
constructor(private _viewCtrl: ViewController) {}
dismiss() {
this._viewCtrl.dismiss();
}
}

View File

@ -1,52 +0,0 @@
// our root app component
import {Component, Compiler, ViewContainerRef, ViewChild, Input, ComponentRef, ComponentFactoryResolver, ChangeDetectorRef} from '@angular/core'
// Helper component to add dynamic components
@Component({
selector: 'component-loader',
template: `<div #target></div>`
})
export class ComponentLoader {
@ViewChild('target', { read: ViewContainerRef }) target;
@Input() type;
@Input() data;
cmpRef: ComponentRef<any>;
private isViewInitialized = false;
constructor(private componentFactoryResolver: ComponentFactoryResolver, private compiler: Compiler,
private cdRef: ChangeDetectorRef) { }
updateComponent() {
if (!this.isViewInitialized) {
return;
}
if (this.cmpRef) {
this.cmpRef.destroy();
}
const factory = this.componentFactoryResolver.resolveComponentFactory(this.type);
this.cmpRef = this.target.createComponent(factory);
// to access the created instance use
// this.compRef.instance.someProperty = 'someValue';
// this.compRef.instance.someOutput.subscribe(val => doSomething());
this.cmpRef.instance.item = this.data;
this.cdRef.detectChanges();
}
ngOnChanges() {
this.updateComponent();
}
ngAfterViewInit() {
this.isViewInitialized = true;
this.updateComponent();
}
ngOnDestroy() {
if (this.cmpRef) {
this.cmpRef.destroy();
}
}
}

View File

@ -8,7 +8,9 @@
<br>
<p>{{cardItem.qry}}</p>
</ion-card-content>
<button ion-button icon-start clear small (click)="close()">
<ion-icon name="close-circle"></ion-icon>
<div>Close</div>
</button>
<div style="float: right">
<button ion-button icon-center clear (click)="contextMenu()">
<ion-icon name="more"></ion-icon>
</button>
</div>

View File

@ -1,5 +1,9 @@
import { EventEmitter, Component, Input, Output, ElementRef } from '@angular/core';
import { CardItem } from '../../pages/search/search';
import { ProfileService } from '../../services/profile-service';
import { ActionSheetController, AlertController } from 'ionic-angular';
import { PagesService } from '../../services/pages-service';
import { cardContextMenu } from '../../libs/Common';
@Component({
selector: 'error',
@ -13,17 +17,25 @@ export class Error
@Input()
cardItem: CardItem;
constructor(private elementRef: ElementRef)
constructor(
private _elementRef: ElementRef,
private _profileService: ProfileService,
private _actionSheet: ActionSheetController,
private _pagesSvc: PagesService,
private _alertCtrl: AlertController)
{
}
contextMenu() {
cardContextMenu(this._profileService, this._actionSheet, this._pagesSvc, this._alertCtrl, this.cardItem);
}
close(ev) {
let translate = 'translate3d(110%, 0, 0)';
if (ev != null && ev.direction === 2) {
translate = 'translate3d(-110%, 0, 0)';
}
let d = 250;
this.elementRef.nativeElement.parentElement.animate({
this._elementRef.nativeElement.parentElement.animate({
transform: ['none', translate]
}, {
fill: 'forwards',

View File

@ -6,42 +6,37 @@
</ion-item>
<ion-card-content *ngIf="data !== undefined && data.status === 0">
<br>
<ng-template [ngIf]="this.profileService.profile().show_paragraphs"><div class="passage-text" *ngFor="let ch of withParas">
<ng-template [ngIf]="profileService.profile().show_paragraphs"><div class="passage-text" *ngFor="let ch of withParas">
<h2 *ngIf="data.cs.length > 1">
<b>Chapter {{ch.ch}}</b>
</h2>
<div *ngFor="let para of ch.paras">
<h3 *ngIf="this.profileService.profile().show_paragraph_headings && hasHeader(para.p)">{{para.p.h}}</h3>
<h3 *ngIf="profileService.profile().show_paragraph_headings && hasHeader(para.p)">{{para.p.h}}</h3>
<p>
<ng-template ngFor let-vs [ngForOf]="para.vss">
<b *ngIf="this.profileService.profile().show_verse_numbers">{{vs.v}}.</b> <ng-template ngFor let-w [ngForOf]="vs.w">
<b *ngIf="profileService.profile().show_verse_numbers">{{vs.v}}.</b> <ng-template ngFor let-w [ngForOf]="vs.w">
<ng-template [ngIf]="!isPunct(w.t)"> </ng-template><a *ngIf="w.s != null" (click)="openStrongs(w.s)" (press)="openMenu(w.s)">{{w.t}}</a>
<ng-template [ngIf]="w.s == null">{{w.t}}</ng-template>
</ng-template><br *ngIf="this.profileService.profile().verses_on_new_line">
</ng-template><br *ngIf="profileService.profile().verses_on_new_line">
</ng-template>
</p>
</div>
</div></ng-template>
<ng-template [ngIf]="!this.profileService.profile().show_paragraphs"><div class="passage-text" *ngFor="let ch of data.cs">
<ng-template [ngIf]="!profileService.profile().show_paragraphs"><div class="passage-text" *ngFor="let ch of data.cs">
<h2 *ngIf="data.cs.length > 1">
<b>Chapter {{ch.ch}}</b>
</h2>
<ng-template ngFor let-vs [ngForOf]="ch.vss">
<b *ngIf="this.profileService.profile().show_verse_numbers">{{vs.v}}.</b> <ng-template ngFor let-w [ngForOf]="vs.w">
<b *ngIf="profileService.profile().show_verse_numbers">{{vs.v}}.</b> <ng-template ngFor let-w [ngForOf]="vs.w">
<ng-template [ngIf]="!isPunct(w.t)"> </ng-template><a *ngIf="w.s != null" (click)="openStrongs(w.s)" (press)="openMenu(w.s)">{{w.t}}</a>
<ng-template [ngIf]="w.s == null">{{w.t}}</ng-template>
</ng-template><br *ngIf="this.profileService.profile().verses_on_new_line">
</ng-template><br *ngIf="profileService.profile().verses_on_new_line">
</ng-template>
</div></ng-template>
</ion-card-content>
<ion-card-content *ngIf="data !== undefined && data.status === -1">
<error-message [msg]="data.msg"></error-message>
</ion-card-content>
<div style="float: left">
<button ion-button icon-start clear (click)="close()">
<ion-icon name="close-circle"></ion-icon>
</button>
</div>
<div style="float: right">
<button ion-button icon-center clear (click)="prev()" *ngIf="ref !== undefined && ref.Section.start.chapter !== '1'">
<ion-icon name="rewind"></ion-icon>
@ -52,7 +47,6 @@
<button ion-button icon-center clear (click)="next()" *ngIf="ref !== undefined && ref.Section.end.chapter !== ref.Section.end.book.last_chapter.toString()">
<ion-icon name="fastforward"></ion-icon>
</button>
<button ion-button icon-center clear (click)="contextMenu()">
<ion-icon name="more"></ion-icon>
</button>

View File

@ -5,6 +5,7 @@ passage {
}
font-family: var(--card-font);
}
passage .button {
@ -17,15 +18,17 @@ passage .button {
passage .passage-text {
padding-bottom: 12px;
h3 {
font-size: 1.6rem;
font-weight: bold;
}
max-width: 600px;
margin: auto;
}
.passage-text + .passage-text {
.passage-text+.passage-text {
border-top: 1px dotted #808080;
padding-top: 12px;
}
@ -46,4 +49,3 @@ passage .passage-text {
// column-rule-width: 1px;
// }
// }

View File

@ -1,11 +1,12 @@
import { Component, EventEmitter, Output, Input, OnInit, ElementRef } from '@angular/core';
import { OpenData, CardItem } from '../../pages/search/search';
import { BiblePassageResult, BibleService, BiblePassage, BibleVerse, HashTable, Paragraph } from '../../services/bible-service';
import { BiblePassageResult, BibleService, BiblePassage, BibleVerse, Paragraph } from '../../services/bible-service';
import { Reference } from '../../libs/Reference';
import { ProfileService } from '../../services/profile-service';
import { ActionSheetController, GestureController, GESTURE_ITEM_SWIPE, AlertController } from 'ionic-angular';
import { ActionSheetController, AlertController } from 'ionic-angular';
import { PagesService } from '../../services/pages-service';
import { GESTURE_PRIORITY_SLIDING_ITEM } from 'ionic-angular/umd/gestures/gesture-controller';
import { cardContextMenu } from '../../libs/Common';
@Component({
selector: 'passage',
@ -24,22 +25,21 @@ export class Passage implements OnInit {
data: BiblePassageResult;
withParas: BibleParaPassage[];
ref: Reference;
private gesture;
constructor(
private bibleService: BibleService,
private elementRef: ElementRef,
private profileService: ProfileService,
private actionSheet: ActionSheetController,
private pagesSvc: PagesService,
private alertCtrl: AlertController
public profileService: ProfileService,
private _elementRef: ElementRef,
private _bibleService: BibleService,
private _actionSheet: ActionSheetController,
private _pagesSvc: PagesService,
private _alertCtrl: AlertController
) {
}
ngOnInit(): void {
this.ref = new Reference(this.cardItem.qry);
this.bibleService.getResultAsPromise(this.ref.Section).then(data => {
this._bibleService.getResultAsPromise(this.ref.Section).then(data => {
this.setData(data);
});
@ -52,88 +52,7 @@ export class Passage implements OnInit {
}
contextMenu() {
const actions = this.actionSheet.create({
title: 'Passage Actions',
buttons: [
{
text: 'Add to a Saved Page',
icon: 'add-circle',
handler: () => {
let btns = this.pagesSvc.getSavedPages().map(p => {
return {
text: p.title,
icon: 'bookmark',
handler: () => {
const page = this.profileService.profile().saved_pages.find(
i => i.title === p.title
);
page.queries.push(this.cardItem);
this.profileService.save();
}
}
});
btns.push({
text: 'Add to New Page',
icon: 'create',
handler: () => {
const alert = this.alertCtrl.create({
title: 'Save Passage as Page',
inputs: [
{
name: 'title',
placeholder: 'Page Title'
}
],
buttons: [
{
text: 'Cancel',
role: 'cancel',
handler: (): void => {
console.log('Cancel clicked');
}
},
{
text: 'Save',
handler: data => {
const p = { queries: this.profileService.profile().items.slice(), title: data.title };
this.profileService.profile().saved_pages.push(p);
this.profileService.save();
this.pagesSvc.addPage(p);
}
}
]
});
alert.present();
}
});
this.actionSheet.create({
title: 'Saved Pages',
buttons: btns
}).present();
}
}
]
});
if (this.profileService.isOnSearchPage()) {
actions.addButton({
text: 'Remove from ' + this.profileService.title,
icon: 'remove-circle',
handler: () => {
const page = this.profileService.profile().saved_pages.find(
i =>
i.title === this.profileService.title
);
const idx = this.profileService.profile().items.indexOf(this.cardItem);
this.profileService.profile().items.splice(idx, 1);
page.queries = this.profileService.profile().items.slice();
this.profileService.save();
this.pagesSvc.initializePages(this.profileService.profile().saved_pages);
}
});
}
actions.present();
cardContextMenu(this.profileService, this._actionSheet, this._pagesSvc, this._alertCtrl, this.cardItem);
}
close(ev) {
@ -142,7 +61,7 @@ export class Passage implements OnInit {
translate = 'translate3d(-110%, 0, 0)';
}
let d = 250;
this.elementRef.nativeElement.parentElement.animate({
this._elementRef.nativeElement.parentElement.animate({
transform: ['none', translate]
}, {
fill: 'forwards',
@ -167,7 +86,7 @@ export class Passage implements OnInit {
this.ref.Section.start.verse = '1';
this.ref.Section.end.verse = '*';
this.bibleService.getResultAsPromise(this.ref.Section).then(data => {
this._bibleService.getResultAsPromise(this.ref.Section).then(data => {
this.setData(data);
this.cardItem.qry = data.ref;
this.ref = new Reference(data.ref);
@ -184,7 +103,7 @@ export class Passage implements OnInit {
this.ref.Section.start.verse = '1';
this.ref.Section.end.verse = '*';
this.bibleService.getResultAsPromise(this.ref.Section).then(data => {
this._bibleService.getResultAsPromise(this.ref.Section).then(data => {
this.setData(data);
this.cardItem.qry = data.ref;
this.ref = new Reference(data.ref);
@ -225,7 +144,7 @@ export class Passage implements OnInit {
if (this.ref.Section.start.verse === '0')
this.ref.Section.start.verse = '1';
this.bibleService.getResultAsPromise(this.ref.Section).then(data => {
this._bibleService.getResultAsPromise(this.ref.Section).then(data => {
this.setData(data);
this.cardItem.qry = data.ref;
this.ref = new Reference(data.ref);

View File

@ -0,0 +1,28 @@
<ion-header>
<ion-toolbar>
<ion-title>
<ion-icon name='settings' item-left></ion-icon> Dynamic Bible Settings
</ion-title>
<ion-buttons start>
<button ion-button (click)='dismiss()' large>
<ion-icon name='md-close'></ion-icon>
</button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content padding>
<ng-template [ngIf]='_profileService.profile()'>
<settings></settings>
<h4>Manage Pages</h4>
<ion-list>
<ion-item *ngFor='let p of _profileService.profile().saved_pages'>
{{p.title}}
<button ion-button item-end outline icon-end (click)='removePage(p)'>
Delete Page <ion-icon name='trash'></ion-icon>
</button>
</ion-item>
</ion-list>
</ng-template>
</ion-content>

View File

@ -0,0 +1,17 @@
settings-modal {
.list-header {
font-size: 1.3em;
}
.item-md .item-button {
height: 40px;
}
ion-icon {
float: left;
}
ion-title {
text-align: center;
}
}

View File

@ -0,0 +1,42 @@
import { Component } from '@angular/core';
import { ViewController, AlertController } from 'ionic-angular';
import { ProfileService, SavedPage } from '../../services/profile-service';
@Component({
selector: 'settings-modal',
templateUrl: 'settings-modal.html'
})
export class SettingsModal {
constructor(
private _alertCtrl: AlertController,
private _viewCtrl: ViewController,
private _profileService: ProfileService
) {}
dismiss() {
this._viewCtrl.dismiss();
}
removePage(page: SavedPage) {
let alert = this._alertCtrl.create({
title: 'Confirm Delete',
message: 'Do you want to delete the ' + page.title + ' page?',
buttons: [
{
text: 'Cancel',
role: 'cancel',
handler: () => {
console.log('Cancel clicked');
}
},
{
text: 'Ok',
handler: () => {
this._profileService.removePage(page);
}
}
]
});
alert.present();
}
}

View File

@ -0,0 +1,89 @@
<ion-list>
<ion-list-header>
<ion-icon name='search' item-left></ion-icon>Search Settings
</ion-list-header>
<ion-item>
<ion-label>Show Strongs as Modal</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().strongs_modal' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Clear Search after Query</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().clear_search_after_query' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Append Results Below</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().append_to_bottom' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Insert Result Next to Item</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().insert_next_to_item' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
</ion-list>
<ion-list>
<ion-list-header>
<ion-icon name='browsers' item-left></ion-icon>Display Settings
</ion-list-header>
<ion-item>
<ion-label>Each Verse on New Line</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().verses_on_new_line' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Verse #'s</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().show_verse_numbers' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Paragraphs</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().show_paragraphs' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Paragraph Headings</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().show_paragraph_headings' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
</ion-list>
<ion-list>
<ion-list-header>
<ion-icon name='sync' item-left></ion-icon>Profile Settings
</ion-list-header>
<ion-item>
<ion-label>Sync Search Items</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().sync_search_items' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
</ion-list>
<ion-list>
<ion-list-header>
<ion-icon name='text' item-left></ion-icon>Adjust Text
</ion-list-header>
<ion-item>
<ion-range min='6' max='20' step='1' snaps='true' [(ngModel)]='profileService.profile().font_size' (ionChange)='textSizeChanged()'>
<ion-label range-left class='small-text'>a</ion-label>
<ion-label range-right>A</ion-label>
</ion-range>
</ion-item>
<ion-item>
<ion-label>Font Family</ion-label>
<ion-select [(ngModel)]='profileService.profile().font_family' (ionChange)='fontFamilyChanged()'>
<ion-option value='Roboto, Helvetica, Arial, sans-serif'>Sans Serif</ion-option>
<ion-option value='"Times New Roman", serif'>Times</ion-option>
<ion-option value='Georgia, serif'>Georgia</ion-option>
<ion-option value='Merriweather, serif'>Merriweather</ion-option>
<ion-option value='Consolas, monotype'>Monospaced</ion-option>
<ion-option value='"PT Serif", serif'>PT Serif</ion-option>
</ion-select>
</ion-item>
</ion-list>
<ion-list>
<ion-list-header>
<ion-icon name='log-in' item-left></ion-icon>Login/Logout
</ion-list-header>
<ng-template [ngIf]='!profileService.currentUser()'>
<ion-item center>
<button ion-button block (click)='profileService.authenticate()'>Login With Google</button>
</ion-item>
</ng-template>
<ng-template [ngIf]='profileService.currentUser()'>
<ion-item center>
<button ion-button block (click)='profileService.logout()'>Logout</button>
</ion-item>
</ng-template>
</ion-list>

View File

@ -0,0 +1,3 @@
settings {
}

View File

@ -0,0 +1,30 @@
import { Component } from '@angular/core';
import { ProfileService } from '../../services/profile-service';
@Component({
selector: 'settings',
templateUrl: 'settings.html'
})
export class Settings {
constructor(
public profileService: ProfileService
) {}
textSizeChanged()
{
this.profileService.textSizeChanged();
this.profileService.localSave();
}
fontFamilyChanged()
{
this.profileService.fontFamilyChanged();
this.profileService.localSave();
}
reset()
{
this.profileService.reset()
}
}

View File

@ -1,5 +1,5 @@
import { EventEmitter, Component, Output, OnInit } from '@angular/core';
import { Platform, NavParams, ViewController } from 'ionic-angular';
import { NavParams, ViewController } from 'ionic-angular';
import { Reference } from '../../libs/Reference';
import { StrongsResult, StrongsService } from '../../services/strongs-service';
@ -8,7 +8,7 @@ import { StrongsResult, StrongsService } from '../../services/strongs-service';
templateUrl: 'strongs-modal.html',
providers: [StrongsService]
})
export class StrongsModal implements OnInit
export class StrongsModal implements OnInit
{
sn: number;
dict: string;
@ -17,43 +17,43 @@ export class StrongsModal implements OnInit
@Output()
onItemClicked = new EventEmitter<string>();
constructor(private strongsService: StrongsService,
public platform: Platform,
public params: NavParams,
public viewCtrl: ViewController
)
constructor(
private _strongsService: StrongsService,
private _params: NavParams,
private _viewCtrl: ViewController
)
{
this.sn = this.params.get('sn') as number;
this.dict = this.params.get('dict') as string;
this.onItemClicked.subscribe(item =>
this.sn = this._params.get('sn') as number;
this.dict = this._params.get('dict') as string;
this.onItemClicked.subscribe(item =>
{
let pg = this.params.get('onItemClicked');
let pg = this._params.get('onItemClicked');
pg.updateUIwithItems(item, false);
});
}
ngOnInit(): void
{
this.strongsService.getResultAsPromise(this.sn, this.dict).then(data => this.item = data);
}
dismiss()
ngOnInit(): void
{
this.viewCtrl.dismiss();
this._strongsService.getResultAsPromise(this.sn, this.dict).then(data => this.item = data);
}
openItem(p: string)
dismiss()
{
this._viewCtrl.dismiss();
}
openItem(p: string)
{
this.onItemClicked.emit(p);
this.dismiss();
}
makePassage(p: string)
makePassage(p: string)
{
return Reference.bookName(parseInt(p.split(';')[0])).name + ' ' + p.split(';')[1] + ':' + p.split(';')[2];
}
openPassage(p: string)
openPassage(p: string)
{
let ref = this.makePassage(p);
this.onItemClicked.emit(ref);

View File

@ -1,4 +1,4 @@
<ion-item class="title strongs-title {{data.prefix}}" padding (swipe)="close($event)">
<ion-item class="title strongs-title" padding (swipe)="close($event)">
<ion-icon name="paper" item-left></ion-icon> <span *ngIf="data !== undefined"><span *ngIf="data.status === -1">Error:</span> {{data.prefix}}{{data.sn}}</span>
<button ion-button icon-only item-end large clear (click)="close()">
<ion-icon name="close-circle"></ion-icon>
@ -40,7 +40,9 @@
<ion-card-content *ngIf="data !== undefined && data.status === -1">
<error-message [msg]="data.msg"></error-message>
</ion-card-content>
<button ion-button item-left icon-start clear small (click)="close()">
<ion-icon name="close-circle"></ion-icon>
<div>Close</div>
</button>
<div style="float: right">
<button ion-button icon-center clear (click)="contextMenu()">
<ion-icon name="more"></ion-icon>
</button>
</div>

View File

@ -2,10 +2,7 @@
color: #307e4b;
}
.heb-title {
background-color:#c6efd4;
}
.grk-title {
.strongs-title {
background-color:#a7eebe;
}
strongs {

View File

@ -2,6 +2,10 @@ import { HostListener, EventEmitter, Component, Input, Output, OnInit, AfterView
import { Reference } from '../../libs/Reference';
import { OpenData, CardItem } from '../../pages/search/search';
import { StrongsResult, StrongsService } from '../../services/strongs-service';
import { cardContextMenu } from '../../libs/Common';
import { ProfileService } from '../../services/profile-service';
import { ActionSheetController, AlertController } from 'ionic-angular';
import { PagesService } from '../../services/pages-service';
@Component({
selector: 'strongs',
@ -21,10 +25,20 @@ export class Strongs implements AfterViewChecked, OnInit
data: StrongsResult;
constructor(private strongsService: StrongsService, private elementRef: ElementRef)
constructor(
private _elementRef: ElementRef,
private _strongsService: StrongsService,
private _profileService: ProfileService,
private _actionSheet: ActionSheetController,
private _pagesSvc: PagesService,
private _alertCtrl: AlertController)
{
}
contextMenu() {
cardContextMenu(this._profileService, this._actionSheet, this._pagesSvc, this._alertCtrl, this.cardItem);
}
@HostListener('window:resize', ['$event'])
onResize(evt)
{
@ -45,7 +59,7 @@ export class Strongs implements AfterViewChecked, OnInit
ngOnInit(): void
{
this.strongsService.getResultAsPromise(parseInt(this.cardItem.qry), this.cardItem.dict)
this._strongsService.getResultAsPromise(parseInt(this.cardItem.qry), this.cardItem.dict)
.then(data =>
{
this.data = data;
@ -64,7 +78,7 @@ export class Strongs implements AfterViewChecked, OnInit
translate = 'translate3d(-110%, 0, 0)';
}
let d = 250;
this.elementRef.nativeElement.parentElement.animate({
this._elementRef.nativeElement.parentElement.animate({
transform: ['none', translate]
}, {
fill: 'forwards',

View File

@ -1,5 +1,5 @@
import { EventEmitter, Component, Output, OnInit } from '@angular/core';
import { Platform, NavParams, ViewController } from 'ionic-angular';
import { NavParams, ViewController } from 'ionic-angular';
import { Book, Reference } from '../../libs/Reference';
@Component({
@ -15,16 +15,15 @@ export class VersePickerModal
book: Book;
constructor(
public platform: Platform,
public params: NavParams,
public viewCtrl: ViewController
private _params: NavParams,
private _viewCtrl: ViewController
)
{
this.hasBook = false;
this.books = Reference.Books;
this.onItemClicked.subscribe(item =>
this.onItemClicked.subscribe(item =>
{
let pg = this.params.get('onItemClicked');
let pg = this._params.get('onItemClicked');
pg.updateUIwithItems(item, false);
});
}
@ -37,7 +36,7 @@ export class VersePickerModal
dismiss()
{
this.viewCtrl.dismiss();
this._viewCtrl.dismiss();
}
setBook(book: Book)
{

View File

@ -12,7 +12,9 @@
<ion-card-content *ngIf="data !== undefined && data.status === -1">
<error-message [msg]="data.msg"></error-message>
</ion-card-content>
<button ion-button icon-start clear small (click)="close()">
<ion-icon name="close-circle"></ion-icon>
<div>Close</div>
</button>
<div style="float: right">
<button ion-button icon-center clear (click)="contextMenu()">
<ion-icon name="more"></ion-icon>
</button>
</div>

View File

@ -3,6 +3,10 @@ import { HostListener, EventEmitter, Component, Input, Output, AfterViewChecked,
import { Reference } from '../../libs/Reference';
import { OpenData, CardItem } from '../../pages/search/search';
import { WordLookupResult, WordService } from '../../services/word-service';
import { cardContextMenu } from '../../libs/Common';
import { ProfileService } from '../../services/profile-service';
import { ActionSheetController, AlertController } from 'ionic-angular';
import { PagesService } from '../../services/pages-service';
@Component({
selector: 'words',
@ -24,10 +28,20 @@ export class Words implements AfterViewChecked, OnInit
data: WordLookupResult;
constructor(private wordService: WordService, private elementRef: ElementRef)
constructor(
private _elementRef: ElementRef,
private _wordService: WordService,
private _profileService: ProfileService,
private _actionSheet: ActionSheetController,
private _pagesSvc: PagesService,
private _alertCtrl: AlertController)
{
}
contextMenu() {
cardContextMenu(this._profileService, this._actionSheet, this._pagesSvc, this._alertCtrl, this.cardItem);
}
@HostListener('window:resize', ['$event'])
onResize(evt)
{
@ -75,7 +89,7 @@ export class Words implements AfterViewChecked, OnInit
ngOnInit(): void
{
this.wordService.getResultAsPromise(this.cardItem.qry).then(data =>
this._wordService.getResultAsPromise(this.cardItem.qry).then(data =>
this.data = data
);
}
@ -86,7 +100,7 @@ export class Words implements AfterViewChecked, OnInit
translate = 'translate3d(-110%, 0, 0)';
}
let d = 250;
this.elementRef.nativeElement.parentElement.animate({
this._elementRef.nativeElement.parentElement.animate({
transform: ['none', translate]
}, {
fill: 'forwards',

View File

@ -0,0 +1,95 @@
import { ProfileService } from "../services/profile-service";
import { ActionSheetController, AlertController } from "ionic-angular";
import { PagesService } from "../services/pages-service";
import { CardItem } from "../pages/search/search";
export function cardContextMenu(
profileService: ProfileService,
actionSheet: ActionSheetController,
pagesSvc: PagesService,
alertCtrl: AlertController,
cardItem: CardItem
) {
const actions = actionSheet.create({
title: "Passage Actions",
buttons: [
{
text: "Add to a Saved Page",
icon: "add-circle",
handler: () => {
let btns = pagesSvc.getSavedPages().map(p => {
return {
text: p.title,
icon: "bookmark",
handler: () => {
const page = profileService.profile().saved_pages.find(i => i.title === p.title);
page.queries.push(cardItem);
profileService.save();
}
};
});
btns.push({
text: "Add to New Page",
icon: "create",
handler: () => {
const alert = alertCtrl.create({
title: "Save Passage as Page",
inputs: [
{
name: "title",
placeholder: "Page Title"
}
],
buttons: [
{
text: "Cancel",
role: "cancel",
handler: (): void => {
console.log("Cancel clicked");
}
},
{
text: "Save",
handler: data => {
const p = {
queries: profileService.profile().items.slice(),
title: data.title
};
profileService.profile().saved_pages.push(p);
profileService.save();
pagesSvc.addPage(p);
}
}
]
});
alert.present();
}
});
actionSheet
.create({
title: "Saved Pages",
buttons: btns
})
.present();
}
}
]
});
if (profileService.isOnSearchPage()) {
actions.addButton({
text: "Remove from " + profileService.title,
icon: "remove-circle",
handler: () => {
const page = profileService.profile().saved_pages.find(i => i.title === profileService.title);
const idx = profileService.profile().items.indexOf(cardItem);
profileService.profile().items.splice(idx, 1);
page.queries = profileService.profile().items.slice();
profileService.save();
pagesSvc.initializePages(profileService.profile().saved_pages);
}
});
}
actions.present();
}

View File

@ -0,0 +1,6 @@
export const PageTitles = {
Search: 'Search',
Help: 'Help',
Settings: 'Settings'
}

View File

@ -1,117 +0,0 @@
<ion-header>
<ion-navbar>
<button ion-button icon-only menuToggle left>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>Help</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<h2>How to search for a verse</h2>
<p>
To bring up a passage, just type in a reference, or use the verse picker in the top right corner. Dynamic Bible will recognize most abbreviations of books, and can handle ranges within a book. Here are a few examples to get you started:
</p>
<h3>Examples of Search Items:</h3>
<ul>
<li><b>John 1</b> (displays whole chapter)</li>
<li><b>John 3:16</b> (displays single verse)</li>
<li><b>Jn 3:16</b> (handles abbreviation of John)</li>
<li><b>Jn 3:16-17</b> (displays verses starting with chapter 1 verse 3 through chapter 1 vs 5)</li>
<li><b>John 3:16-4:4</b></li>
<li><b>Jn 3-4</b> (displays both chapters)</li>
<li><b>John 3 - John 4</b> (displays both chapters)</li>
<li><b>Jn 3:1-*</b> (this will get all the verses in the chapter. the * char can be used in verse ranges, but not chapter ranges.)</li>
<li><b>H1234</b> (displays the strongs definition for the Hebrew # 1234)</li>
<li><b>G1234</b> (displays the strongs definition for the Greek # 1234)</li>
<li><b>Jesus</b> (searches for "Jesus". all search terms assume boolean AND, i.e. "Jesus Christ" assumes "Jesus AND Christ". A word without a number is interpreted as search term).</li>
</ul>
We currently don't support ranges that cross book boundaries. If you search for "John 12 - Romans 3" you will get an error. the second book name is ignored.
<h3>Further Notes:</h3>
<ul>
<li>Multiple lookups can be made using a semicolon as a seperator, i.e. "Ruth 1; g1234; spirit").</li>
<li>All greek/hebrew cross references translations are taken from the 1933 Websters version and are sometimes not the same as the KJV translation.</li>
<li>All source materials were taken from the <a href="http://sourceforge.net/projects/zefania-sharp/">Zefania XML Bible Project</a> on SourceForge.</li>
<li>Any Errors and Omission you find would be appreciated. Please contact me via <a href='http://www.jasonwall.org/'>www.jasonwall.org</a>.</li>
</ul>
<h3>Visit Us Online</h3>
<p>
<a href="http://www.dynamicbible.com">www.dynamicbible.com</a> hosts the online version of the web app. We are currently available on the
<a href="https://bitbucket.org/walljm/dynamicbible/downloads/DynamicBible-3.1.0.zip">Windows Platform</a> via the <a href="http://electron.atom.io/">Electron app from atom.io</a>
and on the <a href="https://play.google.com/store/apps/details?id=walljm.dynamicbible">Google Android Play store</a>.
We are working on an IOS distribution and will be releasing that soon.
</p>
<h3>Book Names and Accepted Abbreviations</h3>
<ul>
<li><b>Genesis</b>: gen, ge, gn</li>
<li><b>Exodus</b>: ex, exo, exod, exd</li>
<li><b>Leviticus</b>: lev, le, levi, lv</li>
<li><b>Numbers</b>: num, nu, numb, number</li>
<li><b>Deuteronomy</b>: deut, de, dt, deu</li>
<li><b>Joshua</b>: josh, jos</li>
<li><b>Judges</b>: jud, jdg, judg</li>
<li><b>Ruth</b>: ru</li>
<li><b>1 Samuel</b>: 1, i, 1st, first samuel, sa, sam, sml</li>
<li><b>2 Samuel</b>: 2, ii, 2nd, second, sec samuel, sa, sam, sml</li>
<li><b>1 Kings</b>: 1, i, 1st, first kings, king, kgs, kn, k, ki</li>
<li><b>2 Kings</b>: 2, ii, 2nd, second, sec kings, king, kgs, kn, k, ki</li>
<li><b>1 Chronicles</b>: 1, i, 1st, first chronicles, chron, ch, chr</li>
<li><b>2 Chronicles</b>: 2, ii, 2nd, second, sec chronicles, chron, ch, chr</li>
<li><b>Ezra</b>: ezr</li>
<li><b>Nehemiah</b>: neh, ne, nehamiah</li>
<li><b>Esther</b>: est, es, esth</li>
<li><b>Job</b>: jo, jb</li>
<li><b>Psalms</b>: ps, psa, psalm, psm</li>
<li><b>Proverbs</b>: prov, pr, pro, proverb, prv, prvbs</li>
<li><b>Ecclesiastes</b>: eccl, ecc, eccles, ec, ecl, ecclesiaste</li>
<li><b>Song of Solomon</b>: , song of songs, sos, ss, son, so, song, songs</li>
<li><b>Isaiah</b>: is, isah, isai, ia</li>
<li><b>Jerimiah</b>: jeremiah, jer, je, jere</li>
<li><b>Lamentations</b>: lam, la, lamentation</li>
<li><b>Ezekiel</b>: eze, ezk, ezek</li>
<li><b>Daniel</b>: dan, dn, dl, da</li>
<li><b>Hosea</b>: hos, ho</li>
<li><b>Joel</b>: joe, jl</li>
<li><b>Amos</b>: am, amo</li>
<li><b>Obadiah</b>: oba, ob, obad</li>
<li><b>Jonah</b>: jnh, jon</li>
<li><b>Micah</b>: mic, mi</li>
<li><b>Nahum</b>: nah, na</li>
<li><b>Habakkuk</b>: hab, ha, habakuk</li>
<li><b>Zephaniah</b>: zeph, zep</li>
<li><b>Haggia</b>: hag, hg, haggai</li>
<li><b>Zechariah</b>: zech, zch, zec</li>
<li><b>Malachi</b>: mal</li>
<li><b>Matthew</b>: mt, matt, mat</li>
<li><b>Mark</b>: mrk, mk, mr</li>
<li><b>luke</b>: lu, lke, luk, lk</li>
<li><b>John</b>: jn, jhn</li>
<li><b>Acts</b>: ac, act</li>
<li><b>Romans</b>: rom, ro, rm, roman</li>
<li><b>1 Corinthians</b>: 1, i, 1st, first corinthian, cor, corinthians, corinth, corin, corth, corint</li>
<li><b>2 Corinthians</b>: 2, ii, 2nd, second, sec corinthian, cor, corinthians, corinth, corin, corth, corint</li>
<li><b>Galatians</b>: galatian, galations, gal, ga, gala, galation, galat</li>
<li><b>Ephesians</b>: eph, ep, ephes, ephe, ephs</li>
<li><b>Philippians</b>: phi, phil, ph, philip</li>
<li><b>Colossians</b>: col, co, colossian, colos, coloss</li>
<li><b>1 Thessalonians</b>: 1, i, 1st, first thessalonians, the, thessa, thessalonian, thes, thess, th</li>
<li><b>2 Thessalonians</b>: 2, ii, 2nd, second, sec thessalonians, the, thessa, thessalonian, thes, thess, th</li>
<li><b>1 Timothy</b>: 1, i, 1st, first timothy, tim, ti, timoth, tm</li>
<li><b>2 Timothy</b>: 2, ii, 2nd, second, sec timothy, tim, timoth, tm</li>
<li><b>Titus</b>: tit</li>
<li><b>Philemon</b>: phlmn, phl, phm, phile, philem</li>
<li><b>Hebrews</b>: heb, he, hebrew</li>
<li><b>James</b>: jam, ja, jas, jms, jame, jm</li>
<li><b>1 Peter</b>: 1, i, 1st, first peter, pe, pet, pete, pt, p</li>
<li><b>2 Peter</b>: 2, ii, 2nd, second, sec peter, pe, pet, pete, pt, p</li>
<li><b>1 John</b>: 1, i, 1st, first john, jn, jo</li>
<li><b>2 John</b>: 2, ii, 2nd, second, sec john, jn, jo</li>
<li><b>3 John</b>: 3, iii, 3rd, third john, jn, jo</li>
<li><b>Jude</b>: ju</li>
<li><b>Revelation</b>: rev, re, revelations, rv</li>
</ul>
</ion-content>

View File

@ -1,3 +0,0 @@
page-settings {
}

View File

@ -1,13 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'help',
templateUrl: 'help.html'
})
export class HelpPage
{
constructor()
{
}
}

View File

@ -1,4 +1,4 @@
<ion-menu side="right" [content]="searchcontent" id="actions">
<ion-menu side='right' [content]='searchcontent' id='actions'>
<ion-header>
<ion-toolbar>
<ion-title>Actions</ion-title>
@ -11,130 +11,43 @@
</ion-list-header>
<ion-list>
<button ion-item menuClose="actions" (click)="addPage()">
<ion-icon name="bookmarks" item-left></ion-icon>Save Results as New Page
<button ion-item menuClose='actions' (click)='addPage()'>
<ion-icon name='bookmarks' item-left></ion-icon>Save Results as New Page
</button>
<button *ngIf="this.profileService.isOnSearchPage()" ion-item menuClose="actions" (click)="updatePage()">
<ion-icon name="arrow-up" item-left></ion-icon>Update Page with Results
<button *ngIf='this.profileService.isOnSearchPage()' ion-item menuClose='actions' (click)='updatePage()'>
<ion-icon name='arrow-up' item-left></ion-icon>Update Page with Results
</button>
</ion-list>
<ion-list-header>
<ion-icon name="search" item-left></ion-icon>Search Settings
</ion-list-header>
<ion-item>
<ion-label>Show Strongs as Modal</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().strongs_modal" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Clear Search after Query</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().clear_search_after_query" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Append Results Below</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().append_to_bottom" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Insert Result Next to Item</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().insert_next_to_item" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-list-header>
<ion-icon name="browsers" item-left></ion-icon>Display Settings
</ion-list-header>
<ion-item>
<ion-label>Each Verse on New Line</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().verses_on_new_line" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Verse #'s</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().show_verse_numbers" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Paragraphs</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().show_paragraphs" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Paragraph Headings</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().show_paragraph_headings" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-list-header>
<ion-icon name="sync" item-left></ion-icon>Profile Settings
</ion-list-header>
<ion-item>
<ion-label>Sync Search Items</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().sync_search_items" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-list-header>
<ion-icon name="text" item-left></ion-icon>Adjust Text
</ion-list-header>
<ion-list>
<ion-item>
<ion-range min="6" max="20" step="1" snaps="true" [(ngModel)]="this.profileService.profile().font_size"
(ionChange)="textSizeChanged()">
<ion-label range-left class="small-text">a</ion-label>
<ion-label range-right>A</ion-label>
</ion-range>
</ion-item>
<ion-item>
<ion-label>Font Family</ion-label>
<ion-select [(ngModel)]="this.profileService.profile().font_family" (ionChange)="fontFamilyChanged()">
<ion-option value="Roboto, Helvetica, Arial, sans-serif">Sans Serif</ion-option>
<ion-option value="'Times New Roman', serif">Times</ion-option>
<ion-option value="Georgia, serif">Georgia</ion-option>
<ion-option value="Merriweather, serif">Merriweather</ion-option>
<ion-option value="Consolas, monotype">Monospaced</ion-option>
<ion-option value="'PT Serif'ß, serif">PT Serif</ion-option>
</ion-select>
</ion-item>
</ion-list>
<ion-list-header>
<ion-icon name="log-in" item-left></ion-icon>Login/Logout
</ion-list-header>
<ng-template [ngIf]="!profileService.currentUser()">
<ion-item center>
<button ion-button block (click)="profileService.authenticate()">Login With Google</button>
</ion-item>
</ng-template>
<ng-template [ngIf]="profileService.currentUser()">
<ion-item center>
<button ion-button block (click)="profileService.logout()">Logout</button>
</ion-item>
</ng-template>
<settings></settings>
</ion-content>
</ion-menu>
<ion-header>
<ion-navbar>
<button ion-button icon-only menuToggle left>
<ion-icon name="menu" large></ion-icon>
<ion-icon name='menu' large></ion-icon>
</button>
<ion-auto-complete [dataProvider]="autocompleteService" (search)="getQuery($event)" (input)="setQuery($event)"
(itemSelected)="itemSelected($event)" [options]="{ showCancelButton : 'true' }" #searchbar></ion-auto-complete>
<!--<ion-searchbar (search)="getQuery($event)" (input)="setQuery($event)" [showCancelButton]="true"></ion-searchbar>-->
<ion-auto-complete [dataProvider]='autocompleteService' (search)='getQuery($event)' (input)='setQuery($event)'
(itemSelected)='itemSelected($event)' [options]='{ showCancelButton : "true" }' #searchbar></ion-auto-complete>
<ion-buttons right>
<button ion-button icon-only secondary (click)="versePicker()">
<ion-icon name="albums" large></ion-icon>
<button ion-button icon-only secondary (click)='versePicker()'>
<ion-icon name='albums' large></ion-icon>
</button>
<button ion-button icon-only secondary (click)="actionsMenu()">
<ion-icon name="apps" large></ion-icon>
<button ion-button icon-only secondary (click)='actionsMenu()'>
<ion-icon name='apps' large></ion-icon>
</button>
</ion-buttons>
</ion-navbar>
</ion-header>
<ion-content #searchcontent padding class="search-card">
<ion-card *ngFor="let item of this.profileService.profile().items">
<passage *ngIf="isPassage(item.type)" [cardItem]="item" (onClose)="this.profileService.removeItem($event)"
(onItemClicked)="getItemsNextToCard($event)"></passage>
<strongs *ngIf="isStrongs(item.type)" [cardItem]="item" (onClose)="this.profileService.removeItem($event)"
(onItemClicked)="getItemsNextToCard($event)"></strongs>
<words *ngIf="isWords(item.type)" [cardItem]="item" (onClose)="this.profileService.removeItem($event)"
(onItemClicked)="getItemsNextToCard($event)"></words>
<error *ngIf="isError(item.type)" [cardItem]="item" (onClose)="this.profileService.removeItem($event)"></error>
<ion-content #searchcontent padding class='search-card'>
<ion-card *ngFor='let item of this.profileService.profile().items'>
<passage *ngIf='isPassage(item.type)' [cardItem]='item' (onClose)='this.profileService.removeItem($event)'
(onItemClicked)='getItemsNextToCard($event)'></passage>
<strongs *ngIf='isStrongs(item.type)' [cardItem]='item' (onClose)='this.profileService.removeItem($event)'
(onItemClicked)='getItemsNextToCard($event)'></strongs>
<words *ngIf='isWords(item.type)' [cardItem]='item' (onClose)='this.profileService.removeItem($event)'
(onItemClicked)='getItemsNextToCard($event)'></words>
<error *ngIf='isError(item.type)' [cardItem]='item' (onClose)='this.profileService.removeItem($event)'></error>
</ion-card>
</ion-content>

View File

@ -1,17 +1,18 @@
import { Type, Component, OnInit, ViewChild } from '@angular/core';
import { Loading, LoadingController, ModalController, NavParams, AlertController, MenuController } from 'ionic-angular';
import { Storage } from '@ionic/storage';
import { StrongsModal } from '../../components/strongs-modal/strongs-modal';
import { PagesService } from '../../services/pages-service';
import { ProfileService, User } from './../../services/profile-service';
import { Reference } from '../../libs/Reference';
import { VersePickerModal } from '../../components/verse-picker/verse-picker';
import { AutoCompleteComponent } from 'ionic2-auto-complete';
import { StrongsModal } from '../../components/strongs-modal/strongs-modal';
import { VersePickerModal } from '../../components/verse-picker/verse-picker';
import { Settings } from '../../components/settings/settings';
import { PagesService } from '../../services/pages-service';
import { ProfileService, User } from './../../services/profile-service';
import { SearchAutoCompleteService } from '../../services/search-autocomplete-service';
import { Reference } from '../../libs/Reference';
@Component({
templateUrl: 'search.html',
providers: [SearchAutoCompleteService]

View File

@ -1,96 +0,0 @@
<ion-header>
<ion-navbar>
<button ion-button icon-only menuToggle left>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>Settings</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ng-template [ngIf]="profileService.profile()">
<ion-list-header><ion-icon name="search" item-left></ion-icon>Search Settings</ion-list-header>
<ion-item>
<ion-label>Show Strongs as Modal</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().strongs_modal" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Clear Search after Query</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().clear_search_after_query" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Append Results Below</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().append_to_bottom" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Insert Result Next to Item</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().insert_next_to_item" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-list-header><ion-icon name="browsers" item-left></ion-icon>Display Settings</ion-list-header>
<ion-item>
<ion-label>Each Verse on New Line</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().verses_on_new_line" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Verse #'s</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().show_verse_numbers" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Paragraphs</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().show_paragraphs" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Paragraph Headings</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().show_paragraph_headings" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-list-header><ion-icon name="sync" item-left></ion-icon>Profile Settings</ion-list-header>
<ion-item>
<ion-label>Sync Search Items</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().sync_search_items" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-list-header><ion-icon name="text" item-left></ion-icon>Adjust Text</ion-list-header>
<ion-list>
<ion-item>
<ion-range min="6" max="20" step="1" snaps="true" [(ngModel)]="this.profileService.profile().font_size" (ionChange)="textSizeChanged()">
<ion-label range-left class="small-text">a</ion-label>
<ion-label range-right>A</ion-label>
</ion-range>
</ion-item>
<ion-item>
<ion-label>Font Family</ion-label>
<ion-select [(ngModel)]="this.profileService.profile().font_family" (ionChange)="fontFamilyChanged()">
<ion-option value="Roboto, Helvetica, Arial, sans-serif">Sans Serif</ion-option>
<ion-option value="'Times New Roman', serif">Times</ion-option>
<ion-option value="Georgia, serif">Georgia</ion-option>
<ion-option value="Merriweather, serif">Merriweather</ion-option>
<ion-option value="Consolas, monotype">Monospaced</ion-option>
</ion-select>
</ion-item>
</ion-list>
<ion-list-header><ion-icon name="log-in" item-left></ion-icon>Login/Logout</ion-list-header>
<ng-template [ngIf]="!profileService.currentUser()">
<ion-item>
<button ion-button block (click)="profileService.authenticate()">Login With Google</button>
</ion-item>
</ng-template>
<ng-template [ngIf]="profileService.currentUser()">
<ion-item>
<button ion-button block (click)="profileService.logout()">Logout</button>
</ion-item>
</ng-template>
<h4>Manage Pages</h4>
<ion-list>
<ion-item *ngFor="let p of profileService.profile().saved_pages">
{{p.title}}
<button ion-button item-end outline icon-end (click)="removePage(p)">
Delete Page <ion-icon name="trash"></ion-icon>
</button>
</ion-item>
</ion-list>
</ng-template>
</ion-content>

View File

@ -1,6 +0,0 @@
.list-header {
font-size: 1.3em;
}
.item-md .item-button {
height: 40px;
}

View File

@ -1,61 +0,0 @@
/// <reference path="../../../typings/globals/jquery/index.d.ts" />
import { Component } from '@angular/core';
import { NavController, AlertController } from 'ionic-angular';
import { Storage } from '@ionic/storage';
import { ProfileService, SavedPage } from '../../services/profile-service';
@Component({
selector: 'settings',
templateUrl: 'settings.html',
})
export class SettingsPage
{
constructor(
public navCtrl: NavController
, private alertCtrl: AlertController
, public profileService: ProfileService
) {}
textSizeChanged()
{
this.profileService.textSizeChanged();
this.profileService.localSave();
}
fontFamilyChanged()
{
this.profileService.fontFamilyChanged();
this.profileService.localSave();
}
reset()
{
this.profileService.reset()
}
removePage(page: SavedPage)
{
let alert = this.alertCtrl.create({
title: 'Confirm Delete',
message: 'Do you want to delete the ' + page.title + ' page?',
buttons: [
{
text: 'Cancel',
role: 'cancel',
handler: () =>
{
console.log('Cancel clicked');
}
},
{
text: 'Ok',
handler: () =>
{
this.profileService.removePage(page);
}
}
]
});
alert.present();
}
}

View File

@ -1,40 +1,45 @@
import { Injectable } from '@angular/core';
import { Storage } from '@ionic/storage';
import { SearchPage } from '../pages/search/search';
import { SettingsPage } from '../pages/settings/settings';
import { HelpPage } from '../pages/help/help';
import { SavedPage } from './profile-service';
import { PageTitles } from '../libs/Constants';
import { SettingsModal } from '../components/settings-modal/settings-modal';
import { AboutModal } from '../components/about-modal/about-modal';
@Injectable()
export class PagesService
{
pages: Array<{ title: string, component: any, params: any, icon: string }>;
savedPages: Array<{ title: string, component: any, params: any }>;
pages: Array<Page>;
savedPages: Array<Page>;
constructor(public local: Storage)
{
this.pages = [
{ title: 'Search', component: SearchPage, params: { queries: [], title: 'Search' }, icon: 'search' },
{ title: 'Settings', component: SettingsPage, params: {}, icon: 'settings' },
{ title: 'Help', component: HelpPage, params: {}, icon: 'help-circle' }
{ title: PageTitles.Search, component: SearchPage, params: { queries: [], title: PageTitles.Search }, icon: 'search' },
{ title: PageTitles.Settings, component: SettingsModal, params: {}, icon: 'settings' },
{ title: PageTitles.Help, component: AboutModal, params: {}, icon: 'help-circle' }
];
this.savedPages = [];
}
getMainPages(): Array<{ title: string, component: any }>
getMainPages(): Array<Page>
{
return this.pages;
}
getSavedPages(): Array<{ title: string, component: any }>
getSavedPages(): Array<Page>
{
return this.savedPages;
}
addPage(page: SavedPage)
{
this.savedPages.push({ title: page.title, component: SearchPage, params: { queries: page.queries, title: page.title } });
this.savedPages.push({
title: page.title,
component: SearchPage,
params: { queries: page.queries, title: page.title }
});
}
initializePages(page_array: SavedPage[])
@ -43,7 +48,19 @@ export class PagesService
for (let p of page_array)
{
this.savedPages.push({ title: p.title, component: SearchPage, params: { queries: p.queries, title: p.title } });
this.savedPages.push({
title: p.title,
component: SearchPage,
params: { queries: p.queries, title: p.title }
});
}
}
}
export class Page
{
title: string;
component: any;
params: any;
icon?: string;
}

View File

@ -1,4 +1,4 @@
/// <reference path="../../typings/globals/jquery/index.d.ts" />
/// <reference path='../../typings/globals/jquery/index.d.ts' />
import { Injectable } from '@angular/core';
import { AngularFireDatabase, AngularFireObject } from 'angularfire2/database';
import { AngularFireAuth } from 'angularfire2/auth';
@ -16,10 +16,10 @@ import { Output, EventEmitter } from '@angular/core';
export const DEFAULT_USER_NAME = 'john_doe';
@Injectable()
export class ProfileService
{
export class ProfileService {
@Output()
onSavedPagesChanged = new EventEmitter<SavedPage[]>();
@Output()
onLocalStorageLoaded = new EventEmitter<User>();
@ -34,32 +34,24 @@ export class ProfileService
last: CardItem;
title: string;
constructor(
private local: Storage,
private db: AngularFireDatabase,
public firebaseAuth: AngularFireAuth
)
{
constructor(private local: Storage, private db: AngularFireDatabase, public firebaseAuth: AngularFireAuth) {
this.url = document.URL;
this.isWeb = (document.URL.startsWith('http') && !document.URL.startsWith('http://localhost:8080'));
this.isWeb = document.URL.startsWith('http') && !document.URL.startsWith('http://localhost:8080');
this.localIsLoaded = false;
// asyncrounosly kick off a poller that does the work of syncing remotely when the
// profile needs to be synced.
(function poll(self)
{
setTimeout(function ()
{
(function poll(self) {
setTimeout(function() {
// Setup the next poll recursively
if (self.needsSync)
{
if (self.needsSync) {
// do the sync here.
// If we have a remote profile then save it there too
if (self.remoteProfile && self.localProfile.uid)
{
let st = new Date(); console.log('Saving the remote profile...');
// If we have a remote profile then save it there too
if (self.remoteProfile && self.localProfile.uid) {
let st = new Date();
console.log('Saving the remote profile...');
self.remoteProfile.ref.set(self.localProfile);
console.log(' Finished saving remote profile. ' + self.elapsed(st, new Date) + 'ms');
console.log(' Finished saving remote profile. ' + self.elapsed(st, new Date()) + 'ms');
}
self.needsSync = false;
}
@ -68,12 +60,10 @@ export class ProfileService
}, 2000);
})(this);
this.local.get('profile').then(json_profile =>
{
this.local.get('profile').then(json_profile => {
let t = this.profile();
if (json_profile !== null)
t = JSON.parse(json_profile);
if (json_profile !== null) t = JSON.parse(json_profile);
this.localProfile = t;
this.localIsLoaded = true;
@ -81,9 +71,9 @@ export class ProfileService
});
this.firebaseAuth.authState.subscribe(state => this.subscribeToRemoteProfile(this.db, state));
}
//#region Profile
removeItem(item) {
const idx = this.profile().items.indexOf(item);
@ -98,42 +88,36 @@ export class ProfileService
if (this.last != null && this.profile().insert_next_to_item) {
const idx = this.profile().items.indexOf(this.last);
this.profile().items.splice(idx + 1, 0, item);
} else
this.profile().items.push(item);
}
else {
} else this.profile().items.push(item);
} else {
if (this.last != null && this.profile().insert_next_to_item) {
const idx = this.profile().items.indexOf(this.last);
this.profile().items.splice(idx, 0, item);
} else
this.profile().items.unshift(item);
} else this.profile().items.unshift(item);
}
this.last = null;
}
isOnSearchPage(){
isOnSearchPage() {
return this.title !== 'Search';
}
profile(): User
{
if (!this.localProfile)
{
profile(): User {
if (!this.localProfile) {
this.localProfile = ProfileService.createDefaultUser();
}
return this.localProfile;
}
subscribeToRemoteProfile(db: AngularFireDatabase, user: firebase.User)
{
subscribeToRemoteProfile(db: AngularFireDatabase, user: firebase.User) {
console.log('subscribeToRemoteProfile');
if (!user || this.firebaseUser) return;
console.log('You got the firebase user.');
let obj = db.object('/settings/' + user.uid);
this.remoteProfile = {
ref: obj as AngularFireObject<User>,
stream: obj.valueChanges() as Observable<User>,
stream: obj.valueChanges() as Observable<User>
};
this.firebaseUser = user;
this.profile().username = user.displayName;
@ -141,166 +125,132 @@ export class ProfileService
this.remoteProfile.stream.subscribe(
user => this.handleRemotePreferenceChange(user),
error => console.log(error));
error => console.log(error)
);
}
comparePage(a: SavedPage, b: SavedPage)
{
if (a.title > b.title)
return 1;
if (a.title === b.title)
return 0;
if (a.title < b.title)
return -1;
comparePage(a: SavedPage, b: SavedPage) {
if (a.title > b.title) return 1;
if (a.title === b.title) return 0;
if (a.title < b.title) return -1;
}
handleRemotePreferenceChange(user: User)
{
handleRemotePreferenceChange(user: User) {
console.log('handleRemotePreferenceChange');
if (user)
{
if (user) {
let changed = false;
if (user.saved_pages !== undefined)
{
if (user.saved_pages !== undefined) {
this.localProfile.saved_pages = user.saved_pages;
changed = true;
}
if (this.profile().sync_search_items)
{
if (user === undefined || user.items === undefined)
{
if (this.profile().sync_search_items) {
if (user === undefined || user.items === undefined) {
this.localProfile.items = [];
}
else
{
} else {
this.localProfile.items = user.items;
}
changed = true;
}
// don't sync things that don't make sense.
if (user.uid !== undefined && this.profile().uid !== user.uid)
{
if (user.uid !== undefined && this.profile().uid !== user.uid) {
this.profile().uid = user.uid;
changed = true;
}
if (user.username !== undefined && this.profile().username !== user.username)
{
if (user.username !== undefined && this.profile().username !== user.username) {
this.profile().username = user.username;
changed = true;
}
// We only save the local change here since this is an update from our remote profile.
if (changed)
{
if (changed) {
this.localSave();
}
this.onSavedPagesChanged.emit(user.saved_pages);
}
else
{
} else {
this.save();
}
}
currentUser(): firebase.User
{
currentUser(): firebase.User {
return this.firebaseAuth.auth.currentUser;
}
authenticate()
{
authenticate() {
console.log('Authenticating to remote...');
let self = this;
let provider = new firebase.auth.GoogleAuthProvider();
if (this.isWeb)
{
if (this.isWeb) {
this.firebaseAuth.auth.signInWithPopup(provider);
}
else
{
firebase.auth().signInWithRedirect(provider).then(function ()
{
return firebase.auth().getRedirectResult();
}).catch(function (error)
{
// Handle Errors here.
console.log(error);
});
} else {
firebase
.auth()
.signInWithRedirect(provider)
.then(function() {
return firebase.auth().getRedirectResult();
})
.catch(function(error) {
// Handle Errors here.
console.log(error);
});
}
this.firebaseAuth.authState.subscribe(state => this.subscribeToRemoteProfile(this.db, state));
this.remoteLoggedIn = true;
}
refresh()
{
refresh() {
console.log('refresh');
this.logout();
this.authenticate();
}
logout()
{
logout() {
console.log('logout');
this.firebaseAuth.auth.signOut(); // sign out
this.remoteProfile = null; // inform the profile service not to bother
this.remoteLoggedIn = false;
}
save()
{
save() {
this.localSave();
this.needsSync = true;
}
localSave()
{
localSave() {
console.log('saving local');
this.local.set('profile', JSON.stringify(this.profile()));
}
private elapsed(start: Date, finish: Date)
{
private elapsed(start: Date, finish: Date) {
let difference = new Date();
difference.setTime(finish.getTime() - start.getTime());
return difference.getMilliseconds();
}
// this function updates a user object, in case new properties have been introduced
// in a release.
update(t: User): boolean
{
// in a release.
update(t: User): boolean {
let updated = false;
let k;
const user = this.profile();
for (k in user)
{
if (user.hasOwnProperty(k))
{
if (t[k] === undefined)
{
for (k in user) {
if (user.hasOwnProperty(k)) {
if (t[k] === undefined) {
t[k] = user[k];
updated = true;
}
}
}
for (k in user)
if (user.hasOwnProperty(k))
user[k] = t[k];
for (k in user) if (user.hasOwnProperty(k)) user[k] = t[k];
this.textSizeChanged();
this.fontFamilyChanged();
return updated;
}
private resetUser()
{
private resetUser() {
this.profile().strongs_modal = true;
this.profile().clear_search_after_query = false;
this.profile().items = [];
@ -315,15 +265,13 @@ export class ProfileService
this.profile().sync_search_items = false;
}
reset()
{
reset() {
this.resetUser();
this.remoteProfile.ref.set(this.profile())
this.remoteProfile.ref.set(this.profile());
this.save();
}
removePage(page: SavedPage)
{
removePage(page: SavedPage) {
let idx = this.profile().saved_pages.indexOf(page);
this.profile().saved_pages.splice(idx, 1);
this.onSavedPagesChanged.emit(this.localProfile.saved_pages);
@ -333,19 +281,16 @@ export class ProfileService
}
// TODO(jwall): This belongs somewhere else.
textSizeChanged()
{
textSizeChanged() {
$('html').css('font-size', this.profile().font_size + 'px');
}
fontFamilyChanged()
{
document.querySelector('html').style.cssText = '--card-font: '+this.profile().font_family;
fontFamilyChanged() {
document.querySelector('html').style.cssText = '--card-font: ' + this.profile().font_family;
this.textSizeChanged();
}
static createDefaultUser(): User
{
static createDefaultUser(): User {
return {
username: DEFAULT_USER_NAME,
uid: null,
@ -365,34 +310,33 @@ export class ProfileService
sync_search_items: false
};
}
//#endregion
}
type fbObject<T> = {
ref: AngularFireObject<T>,
stream: Observable<T>,
ref: AngularFireObject<T>;
stream: Observable<T>;
};
export type User = {
username: string,
uid: string | null,
strongs_modal: boolean,
clear_search_after_query: boolean,
items: CardItem[],
append_to_bottom: boolean,
insert_next_to_item: boolean,
font_size: number,
font_family: string,
saved_pages: SavedPage[],
verses_on_new_line: boolean,
show_verse_numbers: boolean,
show_paragraphs: boolean,
show_paragraph_headings: boolean,
sync_search_items: boolean
}
username: string;
uid: string | null;
strongs_modal: boolean;
clear_search_after_query: boolean;
items: CardItem[];
append_to_bottom: boolean;
insert_next_to_item: boolean;
font_size: number;
font_family: string;
saved_pages: SavedPage[];
verses_on_new_line: boolean;
show_verse_numbers: boolean;
show_paragraphs: boolean;
show_paragraph_headings: boolean;
sync_search_items: boolean;
};
export type SavedPage = {
queries: CardItem[],
title: string,
}
queries: CardItem[];
title: string;
};

View File

@ -40,7 +40,7 @@
"no-var-keyword": true,
"object-literal-sort-keys": false,
"quotemark": [
true,
false,
"single"
],
"semicolon": [