mirror of
https://gitlab.com/walljm/dynamicbible.git
synced 2025-07-23 07:19:50 -04:00
fix loading saved pages and normal pages,
add settings to savePage, createNote fixed the router, cleaned up the note dialog so it can handle new/existing, added page edit modal
This commit is contained in:
parent
f305e106af
commit
7f5783bf82
@ -5,25 +5,28 @@ import { SearchPage } from './search/components/search-page/search.page';
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'search/',
|
||||
pathMatch: 'full',
|
||||
redirectTo: 'search',
|
||||
},
|
||||
{
|
||||
path: 'search/',
|
||||
path: 'search',
|
||||
pathMatch: 'prefix',
|
||||
component: SearchPage,
|
||||
},
|
||||
{
|
||||
path: 'search/:term',
|
||||
pathMatch: 'prefix',
|
||||
component: SearchPage,
|
||||
},
|
||||
{
|
||||
path: 'saved/:id',
|
||||
pathMatch: 'prefix',
|
||||
component: SearchPage,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
imports: [RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload' })],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AppRoutingModule {}
|
||||
|
@ -13,7 +13,7 @@
|
||||
<a
|
||||
*ngFor="let p of mainPages$ | async"
|
||||
mat-list-item
|
||||
[routerLink]="['/', p.route]"
|
||||
[routerLink]="[p.route]"
|
||||
><mat-icon color="accenovert">{{ p.icon }}</mat-icon> {{ p.title }}</a
|
||||
>
|
||||
</mat-nav-list>
|
||||
@ -23,7 +23,7 @@
|
||||
<a
|
||||
*ngFor="let p of savedPages$ | async"
|
||||
mat-list-item
|
||||
[routerLink]="['/', 'saved', p.id]"
|
||||
[routerLink]="['saved', p.id]"
|
||||
><mat-icon color="accenovert">playlist_play</mat-icon>
|
||||
{{ p.title }}</a
|
||||
>
|
||||
@ -38,6 +38,22 @@
|
||||
position="end"
|
||||
[opened]="false"
|
||||
>
|
||||
<mat-toolbar>Search Settings</mat-toolbar>
|
||||
<mat-nav-list>
|
||||
<mat-list-item>
|
||||
<a mat-list-item (click)="savePage()"
|
||||
><mat-icon color="accenovert">save</mat-icon> Save Results as New
|
||||
Page</a
|
||||
>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item>
|
||||
<a mat-list-item (click)="createNote()"
|
||||
><mat-icon color="accenovert">create</mat-icon> Create a New Note</a
|
||||
>
|
||||
</mat-list-item>
|
||||
</mat-nav-list>
|
||||
|
||||
<app-settings></app-settings>
|
||||
</mat-sidenav>
|
||||
<mat-sidenav-content>
|
||||
|
@ -5,13 +5,19 @@ import { Observable } from 'rxjs';
|
||||
import { Breakpoints, BreakpointObserver } from '@angular/cdk/layout';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
import { MatSidenav } from '@angular/material/sidenav';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { PageEditModalComponent } from './search/components/page-edit-modal/page-edit-modal.component';
|
||||
import { NoteEditModalComponent } from './search/components/note-edit-modal/note-edit-modal.component';
|
||||
import { SavedPage, Page } from './models/app-state';
|
||||
import { Router } from '@angular/router';
|
||||
import { SubscriberComponent } from './common/components/subscriber.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss'],
|
||||
})
|
||||
export class AppComponent implements AfterViewInit {
|
||||
export class AppComponent extends SubscriberComponent implements AfterViewInit {
|
||||
savedPages$ = this.appService.select((state) => state.savedPages);
|
||||
mainPages$ = this.appService.select((state) => state.mainPages);
|
||||
fontSize$ = this.appService.select(
|
||||
@ -34,8 +40,11 @@ export class AppComponent implements AfterViewInit {
|
||||
constructor(
|
||||
private appService: AppService,
|
||||
private navService: NavService,
|
||||
private breakpointObserver: BreakpointObserver
|
||||
private breakpointObserver: BreakpointObserver,
|
||||
private dialog: MatDialog
|
||||
) {
|
||||
super();
|
||||
|
||||
this.appService.initSavedPages();
|
||||
this.appService.initDisplaySettings();
|
||||
|
||||
@ -50,4 +59,12 @@ export class AppComponent implements AfterViewInit {
|
||||
ngAfterViewInit(): void {
|
||||
this.navService.setSidenav(this.sidenav, this.settings);
|
||||
}
|
||||
|
||||
savePage() {
|
||||
this.dialog.open(PageEditModalComponent);
|
||||
}
|
||||
|
||||
createNote() {
|
||||
this.dialog.open(NoteEditModalComponent);
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ 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 { PageEditModalComponent } from './search/components/page-edit-modal/page-edit-modal.component';
|
||||
import { NoteEditModalComponent } from './search/components/note-edit-modal/note-edit-modal.component';
|
||||
import { VersePickerModalComponent } from './search/components/verse-picker/verse-picker-modal.component';
|
||||
|
||||
@ -64,6 +65,7 @@ import { ClipboardModule } from '@angular/cdk/clipboard';
|
||||
StrongsComponent,
|
||||
WordsComponent,
|
||||
NoteComponent,
|
||||
PageEditModalComponent,
|
||||
NoteEditModalComponent,
|
||||
VersePickerModalComponent,
|
||||
SettingsComponent,
|
||||
@ -78,6 +80,7 @@ import { ClipboardModule } from '@angular/cdk/clipboard';
|
||||
BrowserAnimationsModule,
|
||||
|
||||
NgxMdModule.forRoot(),
|
||||
|
||||
MatSidenavModule,
|
||||
MatToolbarModule,
|
||||
MatIconModule,
|
||||
|
@ -1,5 +1,7 @@
|
||||
export interface AppState {
|
||||
readonly currentPage: SavedPage;
|
||||
readonly savedPages: readonly SavedPage[];
|
||||
readonly savedPagesLoaded: boolean;
|
||||
readonly mainPages: readonly Page[];
|
||||
readonly cards: readonly CardItem[];
|
||||
readonly autocomplete: readonly string[];
|
||||
|
@ -2,15 +2,6 @@
|
||||
<mat-toolbar>
|
||||
<mat-icon>edit</mat-icon>
|
||||
<div class="title">Edit: {{ data.title }}</div>
|
||||
<span class="close-button">
|
||||
<button
|
||||
mat-icon-button
|
||||
mat-dialog-close
|
||||
aria-label="Exit the verse picker"
|
||||
>
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</button>
|
||||
</span>
|
||||
</mat-toolbar>
|
||||
</div>
|
||||
<mat-dialog-content>
|
||||
|
@ -3,6 +3,7 @@ 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';
|
||||
import { UUID } from 'angular2-uuid';
|
||||
|
||||
@Component({
|
||||
selector: 'app-note-edit-modal',
|
||||
@ -12,6 +13,7 @@ import { AppService } from '../../../services/app.service';
|
||||
export class NoteEditModalComponent {
|
||||
noteForm: FormGroup;
|
||||
data: NoteItem;
|
||||
isNew = false;
|
||||
|
||||
constructor(
|
||||
@Inject(MAT_DIALOG_DATA) public cardItem: CardItem,
|
||||
@ -19,7 +21,17 @@ export class NoteEditModalComponent {
|
||||
private appService: AppService,
|
||||
private fb: FormBuilder
|
||||
) {
|
||||
this.data = cardItem.data as NoteItem;
|
||||
if (cardItem) {
|
||||
this.data = cardItem.data as NoteItem;
|
||||
} else {
|
||||
this.isNew = true;
|
||||
this.data = {
|
||||
id: UUID.UUID(),
|
||||
title: 'Title Here',
|
||||
content: '# Content Here\nIn Markdown format.',
|
||||
xref: '',
|
||||
};
|
||||
}
|
||||
this.noteForm = this.fb.group(this.data);
|
||||
}
|
||||
|
||||
@ -28,17 +40,31 @@ export class NoteEditModalComponent {
|
||||
}
|
||||
|
||||
save() {
|
||||
this.appService.editNote(
|
||||
{
|
||||
...this.cardItem,
|
||||
if (this.isNew) {
|
||||
this.appService.createNote({
|
||||
qry: '',
|
||||
dict: 'n/a',
|
||||
type: 'Note',
|
||||
data: {
|
||||
...this.cardItem.data,
|
||||
...this.data,
|
||||
title: this.noteForm.get('title').value,
|
||||
content: this.noteForm.get('content').value,
|
||||
},
|
||||
},
|
||||
this.cardItem
|
||||
);
|
||||
});
|
||||
} else {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,18 @@
|
||||
<div mat-dialog-title>
|
||||
<mat-toolbar>
|
||||
<mat-icon>save</mat-icon>
|
||||
<div class="title">{{ dialogTitle }}</div>
|
||||
</mat-toolbar>
|
||||
</div>
|
||||
<mat-dialog-content>
|
||||
<form [formGroup]="form">
|
||||
<mat-form-field class="page-title">
|
||||
<mat-label>Title</mat-label>
|
||||
<input formControlName="title" matInput />
|
||||
</mat-form-field>
|
||||
</form>
|
||||
</mat-dialog-content>
|
||||
<div mat-dialog-actions>
|
||||
<button mat-button (click)="save()">Save</button>
|
||||
<button mat-button (click)="cancel()">Cancel</button>
|
||||
</div>
|
@ -0,0 +1,20 @@
|
||||
.close-button {
|
||||
float: right;
|
||||
mat-icon {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
width: 100%;
|
||||
padding-left: 1rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.mat-form-field {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
min-width: 50vw;
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { FormGroup, FormBuilder } from '@angular/forms';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { AppService } from '../../../services/app.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-page-edit-modal',
|
||||
templateUrl: 'page-edit-modal.component.html',
|
||||
styleUrls: ['./page-edit-modal.component.scss'],
|
||||
})
|
||||
export class PageEditModalComponent {
|
||||
form: FormGroup;
|
||||
dialogTitle = 'Save Page using Current Cards';
|
||||
|
||||
constructor(
|
||||
@Inject(MAT_DIALOG_DATA) public title: string,
|
||||
public dialogRef: MatDialogRef<PageEditModalComponent>,
|
||||
private appService: AppService,
|
||||
private fb: FormBuilder
|
||||
) {
|
||||
if (title) {
|
||||
this.dialogTitle = 'Edit Page Name';
|
||||
}
|
||||
|
||||
this.form = this.fb.group({
|
||||
title,
|
||||
});
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
save() {
|
||||
this.appService.savePage(this.form.get('title').value);
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { ActivatedRoute, Router, NavigationEnd } from '@angular/router';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { AppService } from '../../../services/app.service';
|
||||
@ -22,6 +22,9 @@ import {
|
||||
export class SearchPage extends SubscriberComponent implements OnInit {
|
||||
cards$ = this.appService.select((state) => state.cards);
|
||||
suggestions$ = this.appService.select((state) => state.autocomplete);
|
||||
|
||||
savedPagedLoaded = false;
|
||||
|
||||
searchControl = new FormControl();
|
||||
|
||||
@ViewChild(MatAutocomplete)
|
||||
@ -32,25 +35,31 @@ export class SearchPage extends SubscriberComponent implements OnInit {
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private appService: AppService,
|
||||
private router: Router,
|
||||
public navService: NavService,
|
||||
public dialog: MatDialog
|
||||
) {
|
||||
super();
|
||||
// you need to know when certain things are loaded, so you can then do stuff.
|
||||
// this will watch the state and store those items in your class so you can monitor them.
|
||||
this.addSubscription(
|
||||
this.appService.state$.subscribe((state) => {
|
||||
this.savedPagedLoaded = state.savedPagesLoaded;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
// if a route was passed in, perform a search.
|
||||
if (this.activatedRoute.snapshot.paramMap.has('term')) {
|
||||
const term = this.activatedRoute.snapshot.paramMap.get('term');
|
||||
this.search(term);
|
||||
}
|
||||
|
||||
// if a route was passed in, perform a search.
|
||||
if (this.activatedRoute.snapshot.paramMap.has('id')) {
|
||||
const id = this.activatedRoute.snapshot.paramMap.get('id');
|
||||
this.appService.getSavedPage(id);
|
||||
}
|
||||
this.addSubscription(
|
||||
this.router.events.subscribe((evt) => {
|
||||
if (evt instanceof NavigationEnd) {
|
||||
this.init();
|
||||
}
|
||||
console.log(evt);
|
||||
})
|
||||
);
|
||||
|
||||
this.init();
|
||||
// subscribe to autocomplete input control's changes
|
||||
this.addSubscription(
|
||||
this.searchControl.valueChanges.subscribe((value: string) =>
|
||||
@ -59,6 +68,32 @@ export class SearchPage extends SubscriberComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
init() {
|
||||
if (this.activatedRoute.snapshot.paramMap.has('term')) {
|
||||
// if a route was passed in, perform a search.
|
||||
const term = this.activatedRoute.snapshot.paramMap.get('term');
|
||||
this.search(term);
|
||||
} else if (this.activatedRoute.snapshot.paramMap.has('id')) {
|
||||
// if this is a saved page
|
||||
const id = this.activatedRoute.snapshot.paramMap.get('id');
|
||||
this.onSavedPagedLoaded(id);
|
||||
this.navService.close(); // close the nav immediately.
|
||||
} else {
|
||||
// its a blank search. load nothing.
|
||||
}
|
||||
}
|
||||
|
||||
// monitor the saved paged loaded property, and fire the action once it turns true, then stop.
|
||||
onSavedPagedLoaded(id: string) {
|
||||
if (this.savedPagedLoaded) {
|
||||
this.appService.getSavedPage(id);
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.onSavedPagedLoaded(id);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
launchPicker() {
|
||||
this.dialog.open(VersePickerModalComponent);
|
||||
}
|
||||
|
@ -44,7 +44,9 @@ const initialState: AppState = {
|
||||
},
|
||||
],
|
||||
autocomplete: [],
|
||||
currentPage: null,
|
||||
savedPages: [],
|
||||
savedPagesLoaded: false,
|
||||
mainPages: [
|
||||
{ title: PageTitles.Search, icon: PageIcons.Search, route: 'search' },
|
||||
{ title: PageTitles.Settings, icon: PageIcons.Settings, route: 'settings' },
|
||||
@ -71,14 +73,14 @@ const initialState: AppState = {
|
||||
};
|
||||
|
||||
type AppAction =
|
||||
| {
|
||||
type: 'GET_SAVED_PAGES';
|
||||
pages: SavedPage[];
|
||||
}
|
||||
| {
|
||||
type: 'GET_SAVED_PAGE';
|
||||
pageid: string;
|
||||
}
|
||||
| {
|
||||
type: 'UPDATE_SAVED_PAGES';
|
||||
savedPages: SavedPage[];
|
||||
}
|
||||
| {
|
||||
type: 'ADD_CARD';
|
||||
card: CardItem;
|
||||
@ -133,6 +135,13 @@ function reducer(state: AppState, action: AppAction): AppState {
|
||||
autocomplete: [...action.words],
|
||||
};
|
||||
}
|
||||
case 'UPDATE_SAVED_PAGES': {
|
||||
return {
|
||||
...state,
|
||||
savedPagesLoaded: true,
|
||||
savedPages: action.savedPages,
|
||||
};
|
||||
}
|
||||
case 'GET_SAVED_PAGE': {
|
||||
const page = state.savedPages.find(
|
||||
(o) => o.id.toString() === action.pageid
|
||||
@ -144,15 +153,10 @@ function reducer(state: AppState, action: AppAction): AppState {
|
||||
|
||||
return {
|
||||
...state,
|
||||
currentPage: page,
|
||||
cards: [...page.queries],
|
||||
};
|
||||
}
|
||||
case 'GET_SAVED_PAGES': {
|
||||
return {
|
||||
...state,
|
||||
savedPages: [...action.pages],
|
||||
};
|
||||
}
|
||||
case 'ADD_CARD': {
|
||||
let cards = [];
|
||||
|
||||
@ -261,21 +265,17 @@ export class AppService extends createStateService(reducer, initialState) {
|
||||
const exists = await this.localStorageService.has('savedPages').toPromise();
|
||||
|
||||
if (exists) {
|
||||
const pages = await this.getSavedPagesApi();
|
||||
const savedPages = (await this.localStorageService
|
||||
.get('savedPages')
|
||||
.toPromise()) as SavedPage[];
|
||||
|
||||
this.dispatch({
|
||||
type: 'GET_SAVED_PAGES',
|
||||
pages,
|
||||
type: 'UPDATE_SAVED_PAGES',
|
||||
savedPages,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async getSavedPagesApi() {
|
||||
return (await this.localStorageService
|
||||
.get('savedPages')
|
||||
.toPromise()) as SavedPage[];
|
||||
}
|
||||
|
||||
getSavedPage(pageid: string) {
|
||||
this.dispatch({
|
||||
type: 'GET_SAVED_PAGE',
|
||||
@ -283,6 +283,40 @@ export class AppService extends createStateService(reducer, initialState) {
|
||||
});
|
||||
}
|
||||
|
||||
savePage(title: string) {
|
||||
const state = this.getState();
|
||||
|
||||
const savedPages = [
|
||||
...state.savedPages,
|
||||
{
|
||||
// create a new saved page object
|
||||
title,
|
||||
id: UUID.UUID().toString(),
|
||||
queries: [...state.cards],
|
||||
},
|
||||
];
|
||||
|
||||
this.localStorageService.set('savedPages', savedPages).subscribe(
|
||||
// success
|
||||
() => {
|
||||
this.dispatch({
|
||||
type: 'UPDATE_SAVED_PAGES',
|
||||
savedPages,
|
||||
});
|
||||
},
|
||||
// error
|
||||
() => {
|
||||
this.dispatch({
|
||||
type: 'UPDATE_ERROR',
|
||||
error: {
|
||||
// tslint:disable-next-line: quotemark
|
||||
msg: "Something went wrong and the page wasn't saved. :(",
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Display Settings
|
||||
@ -357,6 +391,28 @@ export class AppService extends createStateService(reducer, initialState) {
|
||||
});
|
||||
}
|
||||
|
||||
async createNote(card: CardItem, nextToItem: CardItem = null) {
|
||||
this.saveNoteApi(card.data as NoteItem).subscribe(
|
||||
// success
|
||||
() => {
|
||||
this.dispatch({
|
||||
type: 'ADD_CARD',
|
||||
card,
|
||||
nextToItem,
|
||||
});
|
||||
},
|
||||
// error
|
||||
() => {
|
||||
this.dispatch({
|
||||
type: 'UPDATE_ERROR',
|
||||
error: {
|
||||
// tslint:disable-next-line: quotemark
|
||||
msg: "Something went wrong and the note wasn't saved. :(",
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
async editNote(newCard: CardItem, oldCard: CardItem) {
|
||||
this.saveNoteApi(newCard.data as NoteItem).subscribe(
|
||||
// success
|
||||
|
@ -17,8 +17,9 @@ export class NavService {
|
||||
return this.sidenav.open();
|
||||
}
|
||||
|
||||
public close() {
|
||||
return this.sidenav.close();
|
||||
public async close() {
|
||||
const r = await this.sidenav.close();
|
||||
return r;
|
||||
}
|
||||
|
||||
public toggle(): void {
|
||||
|
Loading…
x
Reference in New Issue
Block a user