,
+ private appService: AppService,
+ private fb: FormBuilder
+ ) {
+ this.data = cardItem.data as NoteItem;
+ this.noteForm = this.fb.group(this.data);
+ }
+
+ cancel() {
+ this.dialogRef.close();
+ }
+
+ save() {
+ this.appService.editNote(
+ {
+ ...this.cardItem,
+ data: {
+ ...this.cardItem.data,
+ title: this.noteForm.get('title').value,
+ content: this.noteForm.get('content').value,
+ },
+ },
+ this.cardItem
+ );
+ this.dialogRef.close();
+ }
+}
diff --git a/app/db/src/app/search/components/note/note.component.html b/app/db/src/app/search/components/note/note.component.html
new file mode 100644
index 00000000..4fc46197
--- /dev/null
+++ b/app/db/src/app/search/components/note/note.component.html
@@ -0,0 +1,51 @@
+
+ {{
+ icon$ | async
+ }}
+ {{ cardItem.data.title }}
+
+
+
+ {{
+ cardItem.data.content
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/db/src/app/search/components/note/note.component.scss b/app/db/src/app/search/components/note/note.component.scss
new file mode 100644
index 00000000..37968379
--- /dev/null
+++ b/app/db/src/app/search/components/note/note.component.scss
@@ -0,0 +1,11 @@
+.note-title {
+ background-color: var(--note-color-primary);
+}
+
+.card-close-button {
+ color: var(--note-color-accent);
+}
+
+.card-actions {
+ color: var(--note-color-primary);
+}
diff --git a/app/db/src/app/search/components/note/note.component.ts b/app/db/src/app/search/components/note/note.component.ts
new file mode 100644
index 00000000..a768f156
--- /dev/null
+++ b/app/db/src/app/search/components/note/note.component.ts
@@ -0,0 +1,40 @@
+import { Component, ViewChild, ElementRef } from '@angular/core';
+import { MatDialog } from '@angular/material/dialog';
+import { NoteEditModalComponent } from '../note-edit-modal/note-edit-modal.component';
+import { CardComponent } from '../../../common/components/card.component';
+import { AppService } from '../../../services/app.service';
+
+@Component({
+ selector: 'app-note',
+ templateUrl: './note.component.html',
+ styleUrls: ['./note.component.scss'],
+})
+export class NoteComponent extends CardComponent {
+ @ViewChild('note') noteElement: ElementRef;
+
+ constructor(
+ protected elementRef: ElementRef,
+ private appService: AppService,
+ public dialog: MatDialog
+ ) {
+ super(elementRef);
+
+ this.icon$ = appService.select((state) => state.cardIcons.note);
+ }
+
+ copy() {
+ const html = this.noteElement.nativeElement.innerHTML;
+ const text = this.noteElement.nativeElement.innerText;
+ this.copyToClip(text, html);
+ }
+
+ edit() {
+ this.dialog.open(NoteEditModalComponent, {
+ data: this.cardItem,
+ });
+ }
+
+ delete() {
+ this.appService.deleteNote(this.cardItem);
+ }
+}
diff --git a/app/db/src/app/search/components/passage/passage.component.scss b/app/db/src/app/search/components/passage/passage.component.scss
index 9b58a676..fefe09fe 100644
--- a/app/db/src/app/search/components/passage/passage.component.scss
+++ b/app/db/src/app/search/components/passage/passage.component.scss
@@ -15,6 +15,6 @@
}
.paragraph-heading {
- font-family: var(--passage-heading-font-family);
+ font-family: var(--card-heading-font-family);
font-weight: 600;
}
diff --git a/app/db/src/app/search/components/passage/passage.component.ts b/app/db/src/app/search/components/passage/passage.component.ts
index fb40c17a..2bc18197 100644
--- a/app/db/src/app/search/components/passage/passage.component.ts
+++ b/app/db/src/app/search/components/passage/passage.component.ts
@@ -1,7 +1,7 @@
import { Component, OnInit, ElementRef, ViewChild } from '@angular/core';
import { BibleReference } from '../../../common/bible-reference';
import { AppService } from '../../../services/app.service';
-import { CardComponent } from '../../../common/card.component';
+import { CardComponent } from '../../../common/components/card.component';
import { Paragraph } from '../../../models/app-state';
@Component({
@@ -28,7 +28,7 @@ export class PassageComponent extends CardComponent implements OnInit {
(state) => state.displaySettings.showVerseNumbers
);
- @ViewChild('passage') passageElement;
+ @ViewChild('passage') passageElement: ElementRef;
constructor(
protected elementRef: ElementRef,
diff --git a/app/db/src/app/search/components/search-page/search.page.html b/app/db/src/app/search/components/search-page/search.page.html
index 74b9c62a..3481e150 100644
--- a/app/db/src/app/search/components/search-page/search.page.html
+++ b/app/db/src/app/search/components/search-page/search.page.html
@@ -55,6 +55,12 @@
(onClose)="removeCard(item)"
(onItemClicked)="getItemsNextToCard($event)"
>
+
diff --git a/app/db/src/app/search/components/search-page/search.page.ts b/app/db/src/app/search/components/search-page/search.page.ts
index 65faeae3..c1fbd20c 100644
--- a/app/db/src/app/search/components/search-page/search.page.ts
+++ b/app/db/src/app/search/components/search-page/search.page.ts
@@ -7,7 +7,7 @@ import { OpenData, CardItem } from 'src/app/models/app-state';
import { BibleReference } from 'src/app/common/bible-reference';
import { MatDialog } from '@angular/material/dialog';
import { VersePickerModalComponent } from '../verse-picker/verse-picker-modal.component';
-import { SubscriberComponent } from '../../../common/subscriber.component';
+import { SubscriberComponent } from '../../../common/components/subscriber.component';
import {
MatAutocompleteTrigger,
diff --git a/app/db/src/app/search/components/strongs/strongs.component.ts b/app/db/src/app/search/components/strongs/strongs.component.ts
index a8bd4c0f..6fa431f7 100644
--- a/app/db/src/app/search/components/strongs/strongs.component.ts
+++ b/app/db/src/app/search/components/strongs/strongs.component.ts
@@ -1,6 +1,6 @@
import { Component, ElementRef, ViewChild } from '@angular/core';
import { AppService } from '../../../services/app.service';
-import { CardComponent } from '../../../common/card.component';
+import { CardComponent } from '../../../common/components/card.component';
@Component({
selector: 'app-strongs',
@@ -9,7 +9,7 @@ import { CardComponent } from '../../../common/card.component';
preserveWhitespaces: true,
})
export class StrongsComponent extends CardComponent {
- @ViewChild('strongs') strongsElement;
+ @ViewChild('strongs') strongsElement: ElementRef;
constructor(
protected elementRef: ElementRef,
diff --git a/app/db/src/app/search/components/words/words.component.ts b/app/db/src/app/search/components/words/words.component.ts
index dc93a193..6b10c39a 100644
--- a/app/db/src/app/search/components/words/words.component.ts
+++ b/app/db/src/app/search/components/words/words.component.ts
@@ -1,6 +1,6 @@
import { Component, ElementRef, ViewChild } from '@angular/core';
import { AppService } from '../../../services/app.service';
-import { CardComponent } from '../../../common/card.component';
+import { CardComponent } from '../../../common/components/card.component';
import { WordLookupResult } from 'src/app/models/app-state';
@Component({
@@ -10,7 +10,7 @@ import { WordLookupResult } from 'src/app/models/app-state';
preserveWhitespaces: true,
})
export class WordsComponent extends CardComponent {
- @ViewChild('words') wordsElement;
+ @ViewChild('words') wordsElement: ElementRef;
constructor(
protected elementRef: ElementRef,
diff --git a/app/db/src/app/services/app.service.ts b/app/db/src/app/services/app.service.ts
index 64ed4169..dda6793a 100644
--- a/app/db/src/app/services/app.service.ts
+++ b/app/db/src/app/services/app.service.ts
@@ -19,14 +19,30 @@ import {
WordLookupResult,
WordToStem,
IndexResult,
+ NoteItem,
+ DisplaySettings,
} from '../models/app-state';
import { Section, BibleReference } from '../common/bible-reference';
import { PageTitles, PageIcons } from '../constants';
import { createStateService } from '../common/state-service';
import * as math from 'mathjs';
+import { UUID } from 'angular2-uuid';
+import { StorageMap } from '@ngx-pwa/local-storage';
const initialState: AppState = {
- cards: [],
+ cards: [
+ {
+ qry: 'UUIDGOESHERE',
+ dict: 'n/a',
+ type: 'Note',
+ data: {
+ id: UUID.UUID(),
+ xref: null,
+ title: 'Title Here',
+ content: '# Content Here\nIn Markdown format.',
+ },
+ },
+ ],
autocomplete: [],
savedPages: [],
mainPages: [
@@ -51,6 +67,7 @@ const initialState: AppState = {
words: 'font_download',
passage: 'menu_book',
strongs: 'article',
+ note: 'text_snippet',
},
};
@@ -92,6 +109,10 @@ type AppAction =
| {
type: 'UPDATE_AUTOCOMPLETE';
words: string[];
+ }
+ | {
+ type: 'UPDATE_DISPLAY_SETTINGS';
+ settings: DisplaySettings;
};
function reducer(state: AppState, action: AppAction): AppState {
@@ -101,6 +122,12 @@ function reducer(state: AppState, action: AppAction): AppState {
}
switch (action.type) {
+ case 'UPDATE_DISPLAY_SETTINGS': {
+ return {
+ ...state,
+ displaySettings: action.settings,
+ };
+ }
case 'UPDATE_AUTOCOMPLETE': {
return {
...state,
@@ -195,7 +222,12 @@ export class AppService extends createStateService(reducer, initialState) {
private searchIndexArray: string[];
private autocomplete: string[];
- constructor(private http: HttpClient) {
+ private readonly dataPath = 'assets/data';
+
+ constructor(
+ private http: HttpClient,
+ private localStorageService: StorageMap
+ ) {
super();
this.searchIndexArray = this.buildIndexArray().sort();
@@ -229,6 +261,131 @@ export class AppService extends createStateService(reducer, initialState) {
});
console.log(msg);
}
+
+ //#region Display Settings
+
+ updateDisplaySettings(settings: DisplaySettings) {
+ this.saveSettingsApi(settings).subscribe(
+ // success
+ () => {
+ this.dispatch({
+ type: 'UPDATE_DISPLAY_SETTINGS',
+ settings,
+ });
+ },
+ // error
+ () => {
+ this.dispatch({
+ type: 'UPDATE_ERROR',
+ error: {
+ // tslint:disable-next-line: quotemark
+ msg: "Something went wrong and the settings weren't saved. :(",
+ },
+ });
+ }
+ );
+ }
+
+ async initDisplaySettings() {
+ const hasDisplaySettings = await this.localStorageService
+ .has('displaySettings')
+ .toPromise();
+
+ if (hasDisplaySettings) {
+ const settings = await this.getSettingsApi();
+
+ this.dispatch({
+ type: 'UPDATE_DISPLAY_SETTINGS',
+ settings,
+ });
+ }
+ }
+
+ private saveSettingsApi(settings: DisplaySettings) {
+ return this.localStorageService.set('displaySettings', settings);
+ }
+
+ private async getSettingsApi() {
+ return (await this.localStorageService
+ .get('displaySettings')
+ .toPromise()) as DisplaySettings;
+ }
+
+ //#endregion
+
+ //#region Notes
+
+ async getNote(qry: string, nextToItem: CardItem = null) {
+ const note = (await this.localStorageService
+ .get('notes/' + qry)
+ .toPromise()) as NoteItem;
+
+ const card = {
+ qry,
+ dict: 'n/a',
+ type: 'Note',
+ data: note,
+ };
+
+ this.dispatch({
+ type: 'ADD_CARD',
+ card,
+ nextToItem,
+ });
+ }
+
+ async editNote(newCard: CardItem, oldCard: CardItem) {
+ this.saveNoteApi(newCard.data as NoteItem).subscribe(
+ // success
+ () => {
+ this.dispatch({
+ type: 'UPDATE_CARD',
+ newCard,
+ oldCard,
+ });
+ },
+ // error
+ () => {
+ this.dispatch({
+ type: 'UPDATE_ERROR',
+ error: {
+ // tslint:disable-next-line: quotemark
+ msg: "Something went wrong and the note wasn't saved. :(",
+ },
+ });
+ }
+ );
+ }
+
+ async deleteNote(noteCard: CardItem) {
+ this.deleteNoteApi(noteCard.data as NoteItem).subscribe(
+ // success
+ () => {
+ this.removeCard(noteCard);
+ },
+ // error
+ () => {
+ this.dispatch({
+ type: 'UPDATE_ERROR',
+ error: {
+ // tslint:disable-next-line: quotemark
+ msg: "Something went wrong and the note wasn't saved. :(",
+ },
+ });
+ }
+ );
+ }
+
+ private deleteNoteApi(note: NoteItem) {
+ return this.localStorageService.delete('notes/' + note.id);
+ }
+
+ private saveNoteApi(note: NoteItem) {
+ return this.localStorageService.set('notes/' + note.id, note);
+ }
+
+ //#endregion
+
//#region Strongs
async getStrongs(
@@ -419,7 +576,7 @@ export class AppService extends createStateService(reducer, initialState) {
try {
const d = await this.http
.get(
- `assets/data/bibles/kjv_strongs/${section.start.book.book_number}-${i}.json`
+ `${this.dataPath}/bibles/kjv_strongs/${section.start.book.book_number}-${i}.json`
)
.toPromise();
chapters.push(d);
@@ -525,7 +682,7 @@ export class AppService extends createStateService(reducer, initialState) {
private async getParagraphMarkers(): Promise> {
const paras = await this.http
- .get>('assets/data/bibles/paras.json')
+ .get>(`${this.dataPath}/bibles/paras.json`)
.toPromise();
this.dispatch({
type: 'UPDATE_PARAGRAPHS',
@@ -631,7 +788,7 @@ export class AppService extends createStateService(reducer, initialState) {
if (stem <= this.searchIndexArray[0]) {
results.unshift(
await this.getSearchReferences(
- 'assets/data/index/' + this.searchIndexArray[0] + 'idx.json',
+ `${this.dataPath}/index/${this.searchIndexArray[0]}idx.json`,
stem
)
);
@@ -647,7 +804,7 @@ export class AppService extends createStateService(reducer, initialState) {
) {
results.unshift(
await this.getSearchReferences(
- 'assets/data/index/' + this.searchIndexArray[w] + 'idx.json',
+ `${this.dataPath}/index/${this.searchIndexArray[w]}idx.json`,
stem
)
);
@@ -692,7 +849,7 @@ export class AppService extends createStateService(reducer, initialState) {
this.wordToStem = new Map();
try {
const r = await this.http
- .get('assets/data/index/word_to_stem_idx.json')
+ .get(`${this.dataPath}/index/word_to_stem_idx.json`)
.toPromise();
// find the right word
@@ -1039,7 +1196,7 @@ export class AppService extends createStateService(reducer, initialState) {
if (!this.autocomplete) {
// if you have't populated the word list yet, do so...
const data = await this.http
- .get('assets/data/index/word_to_stem_idx.json')
+ .get(`${this.dataPath}/index/word_to_stem_idx.json`)
.toPromise();
this.autocomplete = data.map((o) => o.w);
}
diff --git a/app/db/src/styles/app.scss b/app/db/src/styles/app.scss
index 2c9bcfed..9a2c0a88 100644
--- a/app/db/src/styles/app.scss
+++ b/app/db/src/styles/app.scss
@@ -4,6 +4,7 @@ html {
--primary-color: #333;
--card-font: Roboto, Helvetica, Arial, sans-serif;
+ --card-heading-font-family: "Roboto Condensed";
--card-border-radius: 0.2em;
--card-title: #fff;
--card-color: #000;
@@ -11,16 +12,16 @@ html {
--passage-color-primary: rgb(25, 68, 109);
--passage-color-accent: rgb(122, 166, 206);
- --passage-heading-font-family: "Roboto Condensed";
--strongs-color-primary: rgb(17, 70, 29);
--strongs-color-accent: rgb(122, 206, 143);
- --strongs-heading-font-family: "Roboto Condensed";
--words-color-primary: rgb(0, 85, 85);
--words-color-accent: rgb(9, 172, 172);
--words-color-button: rgb(27, 133, 133);
- --words-heading-font-family: "Roboto Condensed";
+
+ --note-color-primary: rgb(71, 1, 54);
+ --note-color-accent: rgb(165, 86, 145);
}
body {
@@ -106,15 +107,34 @@ a {
width: 100%;
}
-mat-h2,
+.mat-h1,
+.mat-headline,
+.mat-typography h1 {
+ font-family: var(--card-heading-font-family) !important;
+ font-size: calc(var(--font-size) * 2) !important;
+ line-height: calc(var(--font-size) * 2.5) !important;
+}
+
+.mat-h2,
.mat-title,
.mat-typography h2 {
+ font-family: var(--card-heading-font-family) !important;
font-size: calc(var(--font-size) * 1.5) !important;
line-height: calc(var(--font-size) * 1.7) !important;
}
+
.mat-h3,
.mat-subheading-2,
.mat-typography h3 {
+ font-family: var(--card-heading-font-family) !important;
font-size: calc(var(--font-size) * 1.1) !important;
line-height: calc(var(--font-size) * 1.2) !important;
}
+
+.mat-h4,
+.mat-subheading-1,
+.mat-typography h4 {
+ font-family: var(--card-heading-font-family) !important;
+ font-size: var(--font-size) !important;
+ line-height: calc(var(--font-size) * 1.1) !important;
+}