mirror of
https://gitlab.com/walljm/dynamicbible.git
synced 2025-07-23 07:19:50 -04:00
basic page admin
bug fix in handling of storage
This commit is contained in:
parent
44072579a1
commit
27569e4c50
@ -1,6 +1,7 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { SearchPage } from './pages/search/search.page';
|
||||
import { SavedPagesAdminPage } from './pages/saved-pages-admin/saved-pages-admin.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@ -19,10 +20,15 @@ const routes: Routes = [
|
||||
component: SearchPage,
|
||||
},
|
||||
{
|
||||
path: 'saved/:id',
|
||||
path: 'page/:id',
|
||||
pathMatch: 'prefix',
|
||||
component: SearchPage,
|
||||
},
|
||||
{
|
||||
path: 'saved/admin',
|
||||
pathMatch: 'prefix',
|
||||
component: SavedPagesAdminPage,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -23,7 +23,7 @@
|
||||
<a
|
||||
*ngFor="let p of savedPages$ | async"
|
||||
mat-list-item
|
||||
[routerLink]="['saved', p.id]"
|
||||
[routerLink]="['page', p.id]"
|
||||
><mat-icon color="accenovert">library_books</mat-icon>
|
||||
{{ p.title }}</a
|
||||
>
|
||||
|
@ -15,7 +15,7 @@ import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
styleUrls: ['./app.component.scss'],
|
||||
})
|
||||
export class AppComponent extends SubscriberBase implements AfterViewInit {
|
||||
savedPages$ = this.appService.select((state) => state.savedPages.value);
|
||||
savedPages$ = this.appService.select((state) => (state.savedPages === null ? null : state.savedPages.value));
|
||||
mainPages$ = this.appService.select((state) => state.mainPages);
|
||||
fontSize$ = this.appService.select((state) => state.displaySettings.value.cardFontSize + 'pt');
|
||||
cardFont$ = this.appService.select((state) => state.displaySettings.value.cardFontFamily);
|
||||
|
@ -51,6 +51,9 @@ import { ClipboardModule } from '@angular/cdk/clipboard';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
import { SavedPagesAdminPage } from './pages/saved-pages-admin/saved-pages-admin.page';
|
||||
import { SavedPageCardComponent } from './components/saved-page-card/saved-page-card.component';
|
||||
|
||||
import { SearchPage } from './pages/search/search.page';
|
||||
|
||||
import { OkCancelModalComponent } from './components/ok-cancel-modal/ok-cancel-modal.component';
|
||||
@ -70,6 +73,8 @@ import { AddToPageModalComponent } from './components/add-to-page-modal/add-to-p
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
SavedPagesAdminPage,
|
||||
SavedPageCardComponent,
|
||||
SearchPage,
|
||||
PassageCardComponent,
|
||||
StrongsComponent,
|
||||
|
@ -0,0 +1,34 @@
|
||||
<div class="card-title pages-title">
|
||||
<mat-icon aria-hidden="false" aria-label="Saved Page Icon">{{
|
||||
icon$ | async
|
||||
}}</mat-icon>
|
||||
<span *ngIf="savedPage">Page: {{ savedPage.title }}</span>
|
||||
</div>
|
||||
<div class="card-content" *ngIf="savedPage">
|
||||
<mat-nav-list>
|
||||
<mat-list-item [disableRipple]="true" *ngFor="let q of savedPage.queries">
|
||||
<span matLine>{{ format(q) }}</span>
|
||||
<button mat-icon-button (click)="onRemoveCard(q)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</mat-list-item>
|
||||
</mat-nav-list>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<span class="card-actions-left"> </span>
|
||||
<span class="card-actions-right">
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="moreMenu"
|
||||
aria-label="Example icon-button with a menu"
|
||||
>
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #moreMenu="matMenu">
|
||||
<button mat-menu-item (click)="onRemovePage()">
|
||||
<mat-icon>delete</mat-icon>
|
||||
<span>Delete Saved Page</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</span>
|
||||
</div>
|
@ -0,0 +1,15 @@
|
||||
.pages-title {
|
||||
background-color: var(--page-color-primary);
|
||||
}
|
||||
|
||||
.card-actions {
|
||||
color: var(--page-color-primary);
|
||||
}
|
||||
|
||||
.card-content {
|
||||
overflow-y: auto;
|
||||
max-height: 25rem;
|
||||
font-family: var(--card-font-family);
|
||||
font-size: var(--card-font-size);
|
||||
padding: 0.5rem;
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
import { Component, ElementRef, ChangeDetectionStrategy, Input, OnInit } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { AppService } from '../../services/app.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { SavedPage } from 'src/app/models/page-state';
|
||||
import { CardItem, CardType } from 'src/app/models/card-state';
|
||||
import { NoteItem } from 'src/app/models/note-state';
|
||||
import { OkCancelModalComponent, OkCancelResult } from '../ok-cancel-modal/ok-cancel-modal.component';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
|
||||
@Component({
|
||||
selector: 'app-saved-page-card',
|
||||
templateUrl: 'saved-page-card.component.html',
|
||||
styleUrls: ['./saved-page-card.component.scss'],
|
||||
preserveWhitespaces: true,
|
||||
})
|
||||
export class SavedPageCardComponent implements OnInit {
|
||||
icon$: Observable<string>;
|
||||
|
||||
@Input()
|
||||
savedPage: SavedPage;
|
||||
|
||||
constructor(protected appService: AppService, public dialog: MatDialog, private snackBar: MatSnackBar) {
|
||||
this.icon$ = appService.select((state) => state.cardIcons.savedPage);
|
||||
}
|
||||
|
||||
format(item: CardItem) {
|
||||
if (item.type === CardType.Note) {
|
||||
return `Note: ${(item.data as NoteItem).title}`;
|
||||
} else if (item.type === CardType.Passage) {
|
||||
return `Passage: ${item.qry}`;
|
||||
} else if (item.type === CardType.Strongs) {
|
||||
return `Strongs: ${item.qry}`;
|
||||
} else if (item.type === CardType.Word) {
|
||||
return `Word Search: ${item.qry}`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
onRemoveCard(card: CardItem) {
|
||||
this.dialog
|
||||
.open(OkCancelModalComponent, {
|
||||
data: {
|
||||
title: 'Delete Card from Saved Page',
|
||||
content: `Do you want to delete this card '${this.format(card)}' from the saved page '${
|
||||
this.savedPage.title
|
||||
}?`,
|
||||
},
|
||||
})
|
||||
.afterClosed()
|
||||
.subscribe((ds: OkCancelResult) => {
|
||||
if (ds.ok) {
|
||||
this.appService.updateSavedPage({
|
||||
...this.savedPage,
|
||||
queries: this.savedPage.queries.filter((o) => o !== card),
|
||||
});
|
||||
this.snackBar.open(`${this.savedPage.title} has been updated!`, '', {
|
||||
duration: 3 * 1000,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onRemovePage() {
|
||||
this.dialog
|
||||
.open(OkCancelModalComponent, {
|
||||
data: {
|
||||
title: 'Delete Saved Page',
|
||||
content: `Do you want to delete the saved page '${this.savedPage.title}?`,
|
||||
},
|
||||
})
|
||||
.afterClosed()
|
||||
.subscribe((ds: OkCancelResult) => {
|
||||
if (ds.ok) {
|
||||
this.appService.removeSavedPage(this.savedPage);
|
||||
this.snackBar.open(`${this.savedPage.title} has been deleted!`, '', {
|
||||
duration: 3 * 1000,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
ngOnInit(): void {
|
||||
console.log(this.savedPage);
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
.card-content {
|
||||
overflow-y: auto;
|
||||
max-height: 25rem;
|
||||
font-family: var(--card-font);
|
||||
font-family: var(--card-font-family);
|
||||
font-size: var(--card-font-size);
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
export const PageTitles = {
|
||||
Search: 'Search',
|
||||
PageAdmin: 'Page Admin',
|
||||
Help: 'Help',
|
||||
};
|
||||
|
||||
export const PageIcons = {
|
||||
Search: 'search',
|
||||
PageAdmin: 'library_books',
|
||||
Help: 'help',
|
||||
};
|
||||
|
||||
|
@ -24,4 +24,5 @@ export interface CardIcons {
|
||||
readonly passage: string;
|
||||
readonly strongs: string;
|
||||
readonly note: string;
|
||||
readonly savedPage: string;
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
<mat-toolbar>
|
||||
<button type="button" mat-icon-button (click)="navService.toggleNav()">
|
||||
<mat-icon md-48 aria-hidden="false" aria-label="Menu Toggle">menu</mat-icon>
|
||||
</button>
|
||||
<span class="page-title">Saved Pages Admin</span>
|
||||
</mat-toolbar>
|
||||
<div class="page-content">
|
||||
<ng-container *ngIf="savedPages$ | async as pages; else nopages">
|
||||
<mat-card *ngFor="let page of pages">
|
||||
<app-saved-page-card [savedPage]="page"></app-saved-page-card>
|
||||
</mat-card>
|
||||
</ng-container>
|
||||
<ng-template #nopages>
|
||||
<span> </span>
|
||||
</ng-template>
|
||||
</div>
|
@ -0,0 +1,20 @@
|
||||
mat-card {
|
||||
max-width: 800px;
|
||||
margin: 1.5rem auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
margin-left: 0.8rem;
|
||||
margin-right: 0.8rem;
|
||||
}
|
||||
|
||||
.page-content {
|
||||
padding: 0 1rem 0 1rem;
|
||||
overflow-y: scroll;
|
||||
height: calc(100vh - 66px);
|
||||
width: calc(100% - 15px);
|
||||
margin-top: 2px;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { AppService } from '../../services/app.service';
|
||||
import { NavService } from '../../services/nav.service';
|
||||
import { SubscriberBase } from '../../common/subscriber-base';
|
||||
|
||||
@Component({
|
||||
selector: 'app-saved-pages-admin',
|
||||
templateUrl: './saved-pages-admin.page.html',
|
||||
styleUrls: ['./saved-pages-admin.page.scss'],
|
||||
})
|
||||
export class SavedPagesAdminPage extends SubscriberBase implements OnInit {
|
||||
savedPages$ = this.appService.select((state) => (state.savedPages === null ? null : state.savedPages.value));
|
||||
|
||||
constructor(public navService: NavService, private appService: AppService, private dialog: MatDialog) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit() {}
|
||||
}
|
@ -54,7 +54,6 @@ export class SearchPage extends SubscriberBase implements OnInit {
|
||||
if (evt instanceof NavigationEnd) {
|
||||
this.init();
|
||||
}
|
||||
console.log(evt);
|
||||
})
|
||||
);
|
||||
|
||||
@ -75,8 +74,6 @@ export class SearchPage extends SubscriberBase implements OnInit {
|
||||
const id = this.activatedRoute.snapshot.paramMap.get('id');
|
||||
this.onSavedPagedLoaded(id);
|
||||
this.navService.closeNav(); // close the nav immediately.
|
||||
} else {
|
||||
// its a blank search. load nothing.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,20 @@ export class AppActionFactory {
|
||||
} as AppAction;
|
||||
}
|
||||
|
||||
static newUpdateSavedPage(savedPage: SavedPage) {
|
||||
return {
|
||||
type: 'UPDATE_SAVED_PAGE',
|
||||
savedPage,
|
||||
} as AppAction;
|
||||
}
|
||||
|
||||
static newRemoveSavedPage(savedPage: SavedPage) {
|
||||
return {
|
||||
type: 'REMOVE_SAVED_PAGE',
|
||||
savedPage,
|
||||
} as AppAction;
|
||||
}
|
||||
|
||||
static newAddCardToSavedPage(card: CardItem, pageId: string) {
|
||||
return {
|
||||
type: 'ADD_CARD_TO_SAVED_PAGE',
|
||||
@ -167,6 +181,14 @@ export type AppAction =
|
||||
type: 'UPDATE_SAVED_PAGES';
|
||||
savedPages: IStorable<readonly SavedPage[]>;
|
||||
}
|
||||
| {
|
||||
type: 'UPDATE_SAVED_PAGE';
|
||||
savedPage: SavedPage;
|
||||
}
|
||||
| {
|
||||
type: 'REMOVE_SAVED_PAGE';
|
||||
savedPage: SavedPage;
|
||||
}
|
||||
| {
|
||||
type: 'ADD_CARD_TO_SAVED_PAGE';
|
||||
card: CardItem;
|
||||
|
@ -22,17 +22,12 @@ export const initialState: AppState = {
|
||||
],
|
||||
autocomplete: [],
|
||||
currentSavedPage: null,
|
||||
savedPages: {
|
||||
createdOn: new Date(0).toISOString(),
|
||||
value: [],
|
||||
},
|
||||
notes: {
|
||||
createdOn: new Date(0).toISOString(),
|
||||
value: [],
|
||||
},
|
||||
savedPages: null,
|
||||
notes: null,
|
||||
savedPagesLoaded: false,
|
||||
mainPages: [
|
||||
{ title: PageTitles.Search, icon: PageIcons.Search, route: 'search' },
|
||||
{ title: PageTitles.PageAdmin, icon: PageIcons.PageAdmin, route: 'saved/admin' },
|
||||
{ title: PageTitles.Help, icon: PageIcons.Help, route: 'help' },
|
||||
],
|
||||
error: null,
|
||||
@ -63,5 +58,6 @@ export const initialState: AppState = {
|
||||
passage: 'menu_book',
|
||||
strongs: 'speaker_notes',
|
||||
note: 'text_snippet',
|
||||
savedPage: 'library_books',
|
||||
},
|
||||
};
|
||||
|
@ -17,8 +17,18 @@ function maybeMutateStorable<T>(
|
||||
incumbant: IStorable<T>,
|
||||
composeState: (item: IStorable<T>) => AppState
|
||||
): AppState {
|
||||
// if the candidate is null, then return the state.
|
||||
if (!candidate) {
|
||||
return state;
|
||||
}
|
||||
|
||||
// if the incumbant is null, then def use the candidate.
|
||||
if (!incumbant) {
|
||||
return composeState(candidate);
|
||||
}
|
||||
|
||||
// only update if the settings are newer.
|
||||
if (new Date(candidate.createdOn) > new Date(incumbant.createdOn)) {
|
||||
if (!incumbant || new Date(candidate.createdOn) > new Date(incumbant.createdOn)) {
|
||||
return composeState(candidate);
|
||||
}
|
||||
|
||||
@ -83,6 +93,36 @@ export function reducer(state: AppState, action: AppAction): AppState {
|
||||
};
|
||||
});
|
||||
}
|
||||
case 'UPDATE_SAVED_PAGE': {
|
||||
const savedPages = new Storable<SavedPage[]>(
|
||||
state.savedPages.value.map((o) => {
|
||||
if (o.id === action.savedPage.id) {
|
||||
return action.savedPage;
|
||||
}
|
||||
return o;
|
||||
})
|
||||
);
|
||||
|
||||
return maybeMutateStorable(state, savedPages, state.savedPages, (item) => {
|
||||
return {
|
||||
...state,
|
||||
savedPagesLoaded: true,
|
||||
savedPages: item,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
case 'REMOVE_SAVED_PAGE': {
|
||||
const savedPages = new Storable<SavedPage[]>(state.savedPages.value.filter((o) => o.id !== action.savedPage.id));
|
||||
|
||||
return maybeMutateStorable(state, savedPages, state.savedPages, (item) => {
|
||||
return {
|
||||
...state,
|
||||
savedPagesLoaded: true,
|
||||
savedPages: item,
|
||||
};
|
||||
});
|
||||
}
|
||||
case 'UPDATE_CURRENT_PAGE': {
|
||||
const savedPages = new Storable<SavedPage[]>([
|
||||
...state.savedPages.value.filter((o) => o.id !== state.currentSavedPage.id),
|
||||
|
@ -111,6 +111,19 @@ export class AppService extends createStateService(reducer, initialState) {
|
||||
});
|
||||
}
|
||||
|
||||
updateSavedPage(savedPage: SavedPage) {
|
||||
this.dispatch({
|
||||
type: 'UPDATE_SAVED_PAGE',
|
||||
savedPage,
|
||||
});
|
||||
}
|
||||
removeSavedPage(savedPage: SavedPage) {
|
||||
this.dispatch({
|
||||
type: 'REMOVE_SAVED_PAGE',
|
||||
savedPage,
|
||||
});
|
||||
}
|
||||
|
||||
addCardToSavedPage(pageId: string, card: CardItem) {
|
||||
this.dispatch({
|
||||
type: 'ADD_CARD_TO_SAVED_PAGE',
|
||||
|
@ -18,7 +18,7 @@ export class StorageService extends SubscriberBase {
|
||||
private displaySettingsRemoteObject: AngularFireObject<IStorable<DisplaySettings>>;
|
||||
|
||||
private savedPagesState$ = this.appService.select((state) => state.savedPages);
|
||||
private savedPagesPath = 'savedPaged';
|
||||
private savedPagesPath = 'savedPages';
|
||||
private savedPagesRemoteObject: AngularFireObject<IStorable<readonly SavedPage[]>>;
|
||||
|
||||
private noteItemsState$ = this.appService.select((state) => state.notes);
|
||||
@ -34,7 +34,7 @@ export class StorageService extends SubscriberBase {
|
||||
|
||||
this.addSubscription(
|
||||
this.displaySettingsState$.subscribe((settings) => {
|
||||
if (!settings) {
|
||||
if (!settings || settings.createdOn === new Date(0).toISOString()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,9 @@ html {
|
||||
|
||||
--note-color-primary: rgb(71, 1, 54);
|
||||
--note-color-accent: rgb(165, 86, 145);
|
||||
|
||||
--page-color-primary: rgb(46, 42, 54);
|
||||
--page-color-accent: rgb(111, 109, 116);
|
||||
}
|
||||
|
||||
body {
|
||||
|
Loading…
x
Reference in New Issue
Block a user