diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..2ca2db7b --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +**/obj/** +**/bin/** +DynamicBibleUtility/packages/Shaman.ValueString.1.0.2.19/.signature.p7s +DynamicBibleUtility/packages/ +DynamicBibleUtility/.vs/ diff --git a/app/db/package-lock.json b/app/db/package-lock.json index e583b48c..be778e08 100644 --- a/app/db/package-lock.json +++ b/app/db/package-lock.json @@ -1567,6 +1567,14 @@ "webpack-sources": "1.4.3" } }, + "@ngx-pwa/local-storage": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@ngx-pwa/local-storage/-/local-storage-10.0.1.tgz", + "integrity": "sha512-Tbp0GpqHD4SWhiJyVBlHwyx6hxZWHc/fA+wclhsmhYBah5+/sDFVla77gR62V1Yjx4vEHDi2fGVo0Ea6jbd+Fw==", + "requires": { + "tslib": "^2.0.0" + } + }, "@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", @@ -2132,6 +2140,11 @@ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" }, + "angular2-uuid": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/angular2-uuid/-/angular2-uuid-1.1.1.tgz", + "integrity": "sha1-cvA81TK39AAy6x7PufhFc4S+lW4=" + }, "ansi-colors": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", @@ -2628,6 +2641,25 @@ "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", "dev": true }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + }, + "dependencies": { + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + } + } + }, "blob": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", @@ -3215,6 +3247,17 @@ "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", "dev": true }, + "clipboard": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz", + "integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==", + "optional": true, + "requires": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, "cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", @@ -3749,7 +3792,7 @@ "integrity": "sha1-PQeYpR5ziZxs5VnZN2Olb8eh2IM=", "requires": { "co": "^3", - "cogent": "git://github.com/timaschew/cogent.git#2246bd071392f5053a3a110024fd608a40a593ba", + "cogent": "git://github.com/timaschew/cogent.git#fix-redirects", "component-consoler": "^2.0.0", "component-validator": "^1.0.0", "debug": "*", @@ -4945,6 +4988,12 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", + "optional": true + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -6415,6 +6464,15 @@ "slash": "^3.0.0" } }, + "good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "optional": true, + "requires": { + "delegate": "^3.1.2" + } + }, "got": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/got/-/got-0.2.0.tgz", @@ -8447,6 +8505,11 @@ "object-visit": "^1.0.0" } }, + "marked": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", + "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==" + }, "mathjs": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-7.1.0.tgz", @@ -8835,6 +8898,13 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "nan": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", + "dev": true, + "optional": true + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -8894,6 +8964,23 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, + "ngx-md": { + "version": "8.1.6", + "resolved": "https://registry.npmjs.org/ngx-md/-/ngx-md-8.1.6.tgz", + "integrity": "sha512-uecStRlxoyOXFeFBNPIsNZDpkOzApWiivQVH4OH/7FvUtIkZrS28dNnmzQvt076t4Fr2UJVxUe8h60dtI1BtuA==", + "requires": { + "marked": "^0.7.0", + "prismjs": "^1.15.0", + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + } + } + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -10652,6 +10739,14 @@ "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", "dev": true }, + "prismjs": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.20.0.tgz", + "integrity": "sha512-AEDjSrVNkynnw6A+B1DsFkd6AVdTnp+/WoUixFRULlCLZVRZlVQMVWio/16jv7G1FscUxQxOQhWwApgbnxr6kQ==", + "requires": { + "clipboard": "^2.0.0" + } + }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", @@ -12026,6 +12121,12 @@ "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", "integrity": "sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ=" }, + "select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=", + "optional": true + }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -14156,6 +14257,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, + "optional": true, "requires": { "arr-flatten": "^1.1.0", "array-unique": "^0.3.2", @@ -14174,6 +14276,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -14206,6 +14309,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, + "optional": true, "requires": { "extend-shallow": "^2.0.1", "is-number": "^3.0.0", @@ -14218,6 +14322,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -14229,7 +14334,11 @@ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "dev": true, - "optional": true + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } }, "glob-parent": { "version": "3.1.0", @@ -14269,6 +14378,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2" }, @@ -14278,6 +14388,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -14289,6 +14400,7 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, + "optional": true, "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -14322,6 +14434,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, + "optional": true, "requires": { "is-number": "^3.0.0", "repeat-string": "^1.6.1" @@ -14812,7 +14925,11 @@ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "dev": true, - "optional": true + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } }, "glob-parent": { "version": "3.1.0", diff --git a/app/db/package.json b/app/db/package.json index f4d2b3e6..a65c0d11 100644 --- a/app/db/package.json +++ b/app/db/package.json @@ -21,9 +21,12 @@ "@angular/platform-browser": "~10.0.4", "@angular/platform-browser-dynamic": "~10.0.4", "@angular/router": "~10.0.4", + "@ngx-pwa/local-storage": "^10.0.1", "@types/mathjs": "^6.0.5", + "angular2-uuid": "^1.1.1", "component": "^1.1.0", "mathjs": "^7.0.2", + "ngx-md": "^8.1.6", "redux": "^4.0.5", "reselect": "^4.0.0", "rxjs": "~6.5.5", diff --git a/app/db/src/app/app.component.html b/app/db/src/app/app.component.html index 353a52ed..aa9b9406 100644 --- a/app/db/src/app/app.component.html +++ b/app/db/src/app/app.component.html @@ -27,12 +27,7 @@ position="end" [opened]="false" > - Settings - - page Test - + diff --git a/app/db/src/app/app.component.ts b/app/db/src/app/app.component.ts index 8240d14c..27f8f048 100644 --- a/app/db/src/app/app.component.ts +++ b/app/db/src/app/app.component.ts @@ -37,6 +37,8 @@ export class AppComponent implements AfterViewInit { private breakpointObserver: BreakpointObserver ) { this.appService.getSavedPages(); + this.appService.initDisplaySettings(); + this.fontSize$.subscribe((size) => { document.documentElement.style.setProperty('--font-size', size); }); diff --git a/app/db/src/app/app.module.ts b/app/db/src/app/app.module.ts index 8c102df2..0f62bfa3 100644 --- a/app/db/src/app/app.module.ts +++ b/app/db/src/app/app.module.ts @@ -7,11 +7,16 @@ import { AppComponent } from './app.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { HttpClientModule } from '@angular/common/http'; +import { NgxMdModule } from 'ngx-md'; + import { SearchPage } from './search/components/search-page/search.page'; import { PassageComponent } from './search/components/passage/passage.component'; import { StrongsComponent } from './search/components/strongs/strongs.component'; import { WordsComponent } from './search/components/words/words.component'; +import { NoteComponent } from './search/components/note/note.component'; +import { SettingsComponent } from './common/components/settings/settings.component'; +import { NoteEditModalComponent } from './search/components/note-edit-modal/note-edit-modal.component'; import { VersePickerModalComponent } from './search/components/verse-picker/verse-picker-modal.component'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -58,7 +63,10 @@ import { ClipboardModule } from '@angular/cdk/clipboard'; PassageComponent, StrongsComponent, WordsComponent, + NoteComponent, + NoteEditModalComponent, VersePickerModalComponent, + SettingsComponent, ], imports: [ BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }), @@ -69,6 +77,7 @@ import { ClipboardModule } from '@angular/cdk/clipboard'; AppRoutingModule, BrowserAnimationsModule, + NgxMdModule.forRoot(), MatSidenavModule, MatToolbarModule, MatIconModule, diff --git a/app/db/src/app/common/card.component.ts b/app/db/src/app/common/components/card.component.ts similarity index 92% rename from app/db/src/app/common/card.component.ts rename to app/db/src/app/common/components/card.component.ts index 745834e2..61fc14a7 100644 --- a/app/db/src/app/common/card.component.ts +++ b/app/db/src/app/common/components/card.component.ts @@ -5,8 +5,8 @@ import { ElementRef, Component, } from '@angular/core'; -import { CardItem, OpenData } from '../models/app-state'; -import { BibleReference } from './bible-reference'; +import { CardItem, OpenData } from '../../models/app-state'; +import { BibleReference } from '../bible-reference'; import { Observable } from 'rxjs'; @Component({ diff --git a/app/db/src/app/common/components/settings/settings.component.html b/app/db/src/app/common/components/settings/settings.component.html new file mode 100644 index 00000000..4e72dfd7 --- /dev/null +++ b/app/db/src/app/common/components/settings/settings.component.html @@ -0,0 +1,60 @@ +Search Settings + + + Show Strongs as Modal + + + Append Results Below + + + + Insert Result Next to Card + + + +Display Settings + + + Show Paragraphs + + + + Show Paragraph Headings + + + + Show Verse Numbers + + + + Show Verses on New Line + + diff --git a/app/db/src/app/common/components/settings/settings.component.scss b/app/db/src/app/common/components/settings/settings.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/app/db/src/app/common/components/settings/settings.component.ts b/app/db/src/app/common/components/settings/settings.component.ts new file mode 100644 index 00000000..c960a06c --- /dev/null +++ b/app/db/src/app/common/components/settings/settings.component.ts @@ -0,0 +1,80 @@ +import { Component } from '@angular/core'; +import { AppService } from 'src/app/services/app.service'; +import { MatSlideToggleChange } from '@angular/material/slide-toggle'; +import { SubscriberComponent } from '../subscriber.component'; +import { DisplaySettings } from 'src/app/models/app-state'; + +@Component({ + selector: 'app-settings', + templateUrl: './settings.component.html', + styleUrls: ['./settings.component.scss'], +}) +export class SettingsComponent extends SubscriberComponent { + displaySettings: DisplaySettings; + + constructor(private appService: AppService) { + super(); + this.addSubscription( + this.appService.state$.subscribe((state) => { + this.displaySettings = state.displaySettings; + }) + ); + } + + //#region Search Settings + + toggleStrongsAsModal(toggle: MatSlideToggleChange) { + this.appService.updateDisplaySettings({ + ...this.displaySettings, + showStrongsAsModal: toggle.checked, + }); + } + + toggleAppendCardToBottom(toggle: MatSlideToggleChange) { + this.appService.updateDisplaySettings({ + ...this.displaySettings, + appendCardToBottom: toggle.checked, + }); + } + + toggleInsertCardNextToItem(toggle: MatSlideToggleChange) { + this.appService.updateDisplaySettings({ + ...this.displaySettings, + insertCardNextToItem: toggle.checked, + }); + } + + //#endregion + + //#region Passage Settings + + toggleParagraphHeadings(toggle: MatSlideToggleChange) { + this.appService.updateDisplaySettings({ + ...this.displaySettings, + showParagraphHeadings: toggle.checked, + }); + } + + toggleParagraphs(toggle: MatSlideToggleChange) { + this.appService.updateDisplaySettings({ + ...this.displaySettings, + showParagraphs: toggle.checked, + }); + } + + toggleVerseNumbers(toggle: MatSlideToggleChange) { + this.appService.updateDisplaySettings({ + ...this.displaySettings, + showVerseNumbers: toggle.checked, + }); + } + + toggleVersesOnNewLine(toggle: MatSlideToggleChange) { + this.appService.updateDisplaySettings({ + ...this.displaySettings, + showVersesOnNewLine: toggle.checked, + }); + } + + //#endregion +} diff --git a/app/db/src/app/common/subscriber.component.ts b/app/db/src/app/common/components/subscriber.component.ts similarity index 100% rename from app/db/src/app/common/subscriber.component.ts rename to app/db/src/app/common/components/subscriber.component.ts diff --git a/app/db/src/app/models/app-state.ts b/app/db/src/app/models/app-state.ts index e7489787..d1aff0d4 100644 --- a/app/db/src/app/models/app-state.ts +++ b/app/db/src/app/models/app-state.ts @@ -1,5 +1,3 @@ -import { MatCardActions } from '@angular/material/card'; - export interface AppState { readonly savedPages: readonly SavedPage[]; readonly mainPages: readonly Page[]; @@ -15,20 +13,27 @@ export interface Error { readonly msg: string; } -export type Data = BiblePassageResult | StrongsResult | WordLookupResult; +export type Data = + | BiblePassageResult + | StrongsResult + | WordLookupResult + | NoteItem; export interface CardIcons { readonly words: string; readonly passage: string; readonly strongs: string; + readonly note: string; } export interface DisplaySettings { readonly showStrongsAsModal: boolean; readonly appendCardToBottom: boolean; readonly insertCardNextToItem: boolean; + readonly fontSize: number; readonly fontFamily: string; + readonly showVersesOnNewLine: boolean; readonly showVerseNumbers: boolean; readonly showParagraphs: boolean; @@ -180,3 +185,18 @@ export interface WordToStem { } //#endregion + +//#region Notes + +export type NoteItem = { + // The Note id + readonly id: string; + // A note title. + readonly title: string; + // An optional cross reference to a bible passage. + readonly xref: string | null; + // The content of the note styled as markdown. + readonly content: string; +}; + +//#endregion diff --git a/app/db/src/app/search/components/note-edit-modal/note-edit-modal.component.html b/app/db/src/app/search/components/note-edit-modal/note-edit-modal.component.html new file mode 100644 index 00000000..dd9c512c --- /dev/null +++ b/app/db/src/app/search/components/note-edit-modal/note-edit-modal.component.html @@ -0,0 +1,31 @@ +
+ + edit +
Edit: {{ data.title }}
+ + + +
+
+ +
+ + Title + + + + Content + + +
+
+
+ + +
diff --git a/app/db/src/app/search/components/note-edit-modal/note-edit-modal.component.scss b/app/db/src/app/search/components/note-edit-modal/note-edit-modal.component.scss new file mode 100644 index 00000000..37e015ef --- /dev/null +++ b/app/db/src/app/search/components/note-edit-modal/note-edit-modal.component.scss @@ -0,0 +1,24 @@ +.close-button { + float: right; + mat-icon { + font-size: 2rem; + } +} + +.title { + width: 100%; + padding-left: 1rem; + font-size: 1.5rem; +} + +.mat-form-field { + display: block; +} + +.note-content { + min-width: 75vw; + + textarea { + min-height: 15rem; + } +} diff --git a/app/db/src/app/search/components/note-edit-modal/note-edit-modal.component.ts b/app/db/src/app/search/components/note-edit-modal/note-edit-modal.component.ts new file mode 100644 index 00000000..50a0844f --- /dev/null +++ b/app/db/src/app/search/components/note-edit-modal/note-edit-modal.component.ts @@ -0,0 +1,44 @@ +import { Component, Inject } from '@angular/core'; +import { FormGroup, FormBuilder } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { CardItem, NoteItem } from '../../../models/app-state'; +import { AppService } from '../../../services/app.service'; + +@Component({ + selector: 'app-note-edit-modal', + templateUrl: 'note-edit-modal.component.html', + styleUrls: ['./note-edit-modal.component.scss'], +}) +export class NoteEditModalComponent { + noteForm: FormGroup; + data: NoteItem; + + constructor( + @Inject(MAT_DIALOG_DATA) public cardItem: CardItem, + public dialogRef: MatDialogRef, + 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; +}