Fix linting errors:

* add redux toolkit so you can use configureStore
* remove usage of withServerTransition and use APP_ID instead
* change copy to use material service, remove html copy
This commit is contained in:
Jason Wall 2024-03-08 13:34:47 -05:00
parent c181fb4504
commit fe382a4113
13 changed files with 175 additions and 76 deletions

View File

@ -1,23 +1,23 @@
{
"root": true,
"ignorePatterns": [
"projects/**/*"
],
"ignorePatterns": ["projects/**/*"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module",
"project": "./tsconfig.json" // <-- Point to your project's "tsconfig.json" or create a new one.
},
"overrides": [
{
"files": [
"*.ts"
],
"files": ["*.ts"],
"parserOptions": {
"project": [
"tsconfig.json",
"e2e/tsconfig.json"
],
"project": ["tsconfig.json", "e2e/tsconfig.json"],
"createDefaultProgram": true
},
"extends": [
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates"
"plugin:@angular-eslint/template/process-inline-templates",
"plugin:deprecation/recommended"
],
"rules": {
"@angular-eslint/directive-selector": [
@ -39,14 +39,9 @@
}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended"
],
"rules": {
}
"files": ["*.html"],
"extends": ["plugin:@angular-eslint/template/recommended"],
"rules": {}
}
]
}

80
src/package-lock.json generated
View File

@ -24,6 +24,7 @@
"@capacitor/ios": "^5.7.0",
"@codetrix-studio/capacitor-google-auth": "^3.4.0-rc.0",
"@ngx-pwa/local-storage": "^17.0.0",
"@reduxjs/toolkit": "^2.2.1",
"angular2-uuid": "^1.1.1",
"component": "^1.1.0",
"firebase": "^10.8.0",
@ -51,9 +52,10 @@
"@types/jasminewd2": "~2.0.13",
"@types/node": "^20.11.21",
"@typescript-eslint/eslint-plugin": "7.1.0",
"@typescript-eslint/parser": "7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"cypress": "latest",
"eslint": "^8.57.0",
"eslint-plugin-deprecation": "^2.0.0",
"firebase-tools": "^13.4.0",
"fuzzy": "^0.1.3",
"inquirer": "^9.2.15",
@ -6378,6 +6380,29 @@
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
},
"node_modules/@reduxjs/toolkit": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.1.tgz",
"integrity": "sha512-8CREoqJovQW/5I4yvvijm/emUiCCmcs4Ev4XPWd4mizSO+dD3g5G6w34QK5AGeNrSH7qM8Fl66j4vuV7dpOdkw==",
"dependencies": {
"immer": "^10.0.3",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"reselect": "^5.0.1"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18",
"react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-redux": {
"optional": true
}
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz",
@ -12616,6 +12641,21 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-plugin-deprecation": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-deprecation/-/eslint-plugin-deprecation-2.0.0.tgz",
"integrity": "sha512-OAm9Ohzbj11/ZFyICyR5N6LbOIvQMp7ZU2zI7Ej0jIc8kiGUERXPNMfw2QqqHD1ZHtjMub3yPZILovYEYucgoQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/utils": "^6.0.0",
"tslib": "^2.3.1",
"tsutils": "^3.21.0"
},
"peerDependencies": {
"eslint": "^7.0.0 || ^8.0.0",
"typescript": "^4.2.4 || ^5.0.0"
}
},
"node_modules/eslint-scope": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.0.tgz",
@ -15602,6 +15642,15 @@
"node": ">=0.10.0"
}
},
"node_modules/immer": {
"version": "10.0.3",
"resolved": "https://registry.npmjs.org/immer/-/immer-10.0.3.tgz",
"integrity": "sha512-pwupu3eWfouuaowscykeckFmVTpqbzW+rXFCX8rQLkZzM9ftBmU/++Ra+o+L27mz03zJTlyV4UUr+fdKNffo4A==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/immutable": {
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz",
@ -21754,6 +21803,14 @@
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="
},
"node_modules/redux-thunk": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
"peerDependencies": {
"redux": "^5.0.0"
}
},
"node_modules/reflect-metadata": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz",
@ -24197,6 +24254,27 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
},
"node_modules/tsutils": {
"version": "3.21.0",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
"integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
"dev": true,
"dependencies": {
"tslib": "^1.8.1"
},
"engines": {
"node": ">= 6"
},
"peerDependencies": {
"typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
}
},
"node_modules/tsutils/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
},
"node_modules/tuf-js": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.0.tgz",

View File

@ -32,6 +32,7 @@
"@capacitor/ios": "^5.7.0",
"@codetrix-studio/capacitor-google-auth": "^3.4.0-rc.0",
"@ngx-pwa/local-storage": "^17.0.0",
"@reduxjs/toolkit": "^2.2.1",
"angular2-uuid": "^1.1.1",
"component": "^1.1.0",
"firebase": "^10.8.0",
@ -59,8 +60,10 @@
"@types/jasminewd2": "~2.0.13",
"@types/node": "^20.11.21",
"@typescript-eslint/eslint-plugin": "7.1.0",
"@typescript-eslint/parser": "7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"cypress": "latest",
"eslint": "^8.57.0",
"eslint-plugin-deprecation": "^2.0.0",
"firebase-tools": "^13.4.0",
"fuzzy": "^0.1.3",
"inquirer": "^9.2.15",
@ -74,8 +77,7 @@
"karma-jasmine-html-reporter": "^2.1.0",
"open": "^10.0.4",
"ts-node": "~10.9.2",
"typescript": "~5.3.3",
"cypress": "latest"
"typescript": "~5.3.3"
},
"browserslist": [
"last 1 Chrome version",

View File

@ -1,7 +1,7 @@
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HttpClientModule } from '@angular/common/http';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { APP_ID, NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MARKED_OPTIONS, MarkdownModule } from 'ngx-markdown';
@ -66,7 +66,6 @@ import { NoteEditModalComponent } from './components/note/edit-modal/note-edit-m
import { VersePickerModalComponent } from './components/verse-picker-modal/verse-picker-modal.component';
import { AddToPageModalComponent } from './components/add-to-page-modal/add-to-page-modal.component';
import { MarkedOptions, MarkedRenderer } from 'ngx-markdown';
// function that returns `MarkedOptions` with renderer override
@ -107,7 +106,7 @@ export function markedOptionsFactory(): MarkedOptions {
OkCancelModalComponent,
],
imports: [
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
BrowserModule,
HttpClientModule,
ReactiveFormsModule,
@ -156,7 +155,7 @@ export function markedOptionsFactory(): MarkedOptions {
MatFormFieldModule,
ClipboardModule,
],
providers: [],
providers: [{ provide: APP_ID, useValue: 'ng-cli-universal' }],
bootstrap: [AppComponent],
})
export class AppModule {}

View File

@ -1,4 +1,5 @@
import { Store, createStore } from 'redux';
import { Store } from 'redux';
import { configureStore } from '@reduxjs/toolkit';
import { BehaviorSubject, Observable } from 'rxjs';
import { distinctUntilChanged, map } from 'rxjs/operators';
@ -16,11 +17,10 @@ class StateService<TState, TAction extends { type: string }> {
private readonly internalState$: BehaviorSubject<TState>;
protected constructor(reducer: (state: TState, action: TAction) => TState, initialState: TState) {
this.store = createStore(
reducer,
initialState as any, // this cast is required by Redux's typings, it should have no impact
undefined // in the future, we may want to add some middleware to the Redux stores. that goes here!
);
this.store = configureStore({
reducer: reducer,
preloadedState: initialState,
});
this.internalState$ = new BehaviorSubject<TState>(initialState as TState);
@ -129,7 +129,6 @@ export function createStateService<TState, TAction extends { type: string }>(
return stateServiceClass as IfImmutable<TState, typeof stateServiceClass, YourStateTypeNeedsToBeImmutable<TState>>;
}
export interface IStateAction<TState, TAction> {
type: TAction;
handle: (state: TState) => TState;

View File

@ -6,6 +6,7 @@ import { MoveDirection } from '../common/move-direction';
import { AddToPageModalComponent } from '../components/add-to-page-modal/add-to-page-modal.component';
import { CardItem } from '../models/card-state';
import { AppService } from '../services/app.service';
import { Clipboard } from '@angular/cdk/clipboard';
@Component({
template: '',
@ -19,19 +20,29 @@ export class CardComponent extends SubscriberBase {
icon$: Observable<string>;
constructor(protected elementRef: ElementRef, protected dialog: MatDialog, protected appService: AppService) {
constructor(
protected elementRef: ElementRef,
protected dialog: MatDialog,
protected appService: AppService,
protected clipboard: Clipboard
) {
super();
}
protected copyToClip(text: string, html: string) {
function listener(e: ClipboardEvent) {
e.clipboardData.setData('text/html', html);
e.clipboardData.setData('text/plain', text);
e.preventDefault();
}
document.addEventListener('copy', listener);
document.execCommand('copy');
document.removeEventListener('copy', listener);
protected copyToClip(text: string) {
this.clipboard.copy(text);
const pending = this.clipboard.beginCopy(text);
let remainingAttempts = 3;
const attempt = () => {
const result = pending.copy();
if (!result && --remainingAttempts) {
setTimeout(attempt);
} else {
// Remember to destroy when you're done!
pending.destroy();
}
};
attempt();
}
close(ev) {

View File

@ -53,16 +53,11 @@ export class NoteEditModalComponent {
//#region cross refs
add(event: MatChipInputEvent): void {
const input = event.input;
const value = event.value;
if ((value || '').trim()) {
this.references.push(value.trim());
}
if (input) {
input.value = '';
}
}
remove(reference: string): void {

View File

@ -1,5 +1,6 @@
import { Component, ViewChild, ElementRef, Input } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Clipboard } from '@angular/cdk/clipboard';
import { CardComponent } from '../card.component';
import { BibleReference } from 'src/app/common/bible-reference';
import { NoteItem } from 'src/app/models/note-state';
@ -22,16 +23,20 @@ export class NoteCardComponent extends CardComponent {
return this.cardItem.data as NoteItem;
}
constructor(protected elementRef: ElementRef, protected appService: AppService, public dialog: MatDialog) {
super(elementRef, dialog, appService);
constructor(
protected elementRef: ElementRef,
protected appService: AppService,
protected clipboard: Clipboard,
public dialog: MatDialog
) {
super(elementRef, dialog, appService, clipboard);
this.icon$ = appService.select((state) => state.settings.value.cardIcons.note);
}
copy() {
const html = this.noteElement.nativeElement.innerHTML;
const text = this.noteElement.nativeElement.innerText;
this.copyToClip(text, html);
this.copyToClip(text);
}
private xrefs: BibleReference[];

View File

@ -1,5 +1,6 @@
import { Component, OnInit, ElementRef, ViewChild, ChangeDetectionStrategy } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Clipboard } from '@angular/cdk/clipboard';
import { NoteItem } from 'src/app/models/note-state';
import { CardComponent } from 'src/app/components/card.component';
import { BibleReference, Overlap } from 'src/app/common/bible-reference';
@ -40,8 +41,13 @@ export class PassageCardComponent extends CardComponent implements OnInit {
return this.cardItem.data as BiblePassageResult;
}
constructor(protected elementRef: ElementRef, protected appService: AppService, public dialog: MatDialog) {
super(elementRef, dialog, appService);
constructor(
protected elementRef: ElementRef,
protected appService: AppService,
protected clipboard: Clipboard,
public dialog: MatDialog
) {
super(elementRef, dialog, appService, clipboard);
this.icon$ = appService.select((state) => state.settings.value.cardIcons.passage);
}
@ -54,9 +60,8 @@ export class PassageCardComponent extends CardComponent implements OnInit {
}
copy() {
const html = this.passageElement.nativeElement.innerHTML + ` - ${this.ref.toString()}`;
const text = this.passageElement.nativeElement.innerText + ` - ${this.ref.toString()}`;
this.copyToClip(text, html);
this.copyToClip(text);
}
next() {

View File

@ -1,5 +1,6 @@
import { Component, ElementRef, ViewChild, OnInit, ChangeDetectionStrategy } from '@angular/core';
import { Component, ElementRef, ViewChild, ChangeDetectionStrategy } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Clipboard } from '@angular/cdk/clipboard';
import { StrongsModalComponent } from '../modal/strongs-modal.component';
import { CardComponent } from 'src/app/components/card.component';
import { BibleReference } from 'src/app/common/bible-reference';
@ -17,12 +18,17 @@ export class StrongsCardComponent extends CardComponent {
asModal = false;
@ViewChild('strongs') strongsElement: ElementRef;
get strongsResult(){
get strongsResult() {
return this.cardItem.data as StrongsResult;
}
constructor(protected elementRef: ElementRef, protected appService: AppService, protected dialog: MatDialog) {
super(elementRef, dialog, appService);
constructor(
protected elementRef: ElementRef,
protected appService: AppService,
protected clipboard: Clipboard,
protected dialog: MatDialog
) {
super(elementRef, dialog, appService, clipboard);
this.icon$ = appService.select((state) => state.settings.value.cardIcons.strongs);
this.addSubscription(
this.appService.state$.subscribe((state) => {
@ -32,9 +38,8 @@ export class StrongsCardComponent extends CardComponent {
}
copy() {
const html = this.strongsElement.nativeElement.innerHTML;
const text = this.strongsElement.nativeElement.innerText;
this.copyToClip(text, html);
this.copyToClip(text);
}
async openStrongs(q: string) {

View File

@ -1,5 +1,6 @@
import { Component, ElementRef, ViewChild, ChangeDetectionStrategy } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Clipboard } from '@angular/cdk/clipboard';
import { BibleReference } from 'src/app/common/bible-reference';
import { CardComponent } from 'src/app/components/card.component';
import { WordLookupResult } from 'src/app/models/words-state';
@ -19,8 +20,13 @@ export class WordsCardComponent extends CardComponent {
return this.cardItem.data as WordLookupResult;
}
constructor(protected elementRef: ElementRef, protected appService: AppService, public dialog: MatDialog) {
super(elementRef, dialog, appService);
constructor(
protected elementRef: ElementRef,
protected appService: AppService,
protected clipboard: Clipboard,
public dialog: MatDialog
) {
super(elementRef, dialog, appService, clipboard);
this.icon$ = appService.select((state) => state.settings.value.cardIcons.words);
}
@ -29,9 +35,8 @@ export class WordsCardComponent extends CardComponent {
BibleReference.makePassageFromReferenceKey(ref)
);
const html = refs.map((ref) => `<a href='http://dynamicbible.com/search/${ref}'>${ref}</a>`).join(', ');
const text = refs.join(', ');
this.copyToClip(text, html);
this.copyToClip(text);
}
makePassage(p: string) {

View File

@ -865,7 +865,7 @@ export class AppService extends createStateService(appReducer, initialState) {
} else if (qry.search(/(H|G)[0-9]/i) !== -1) {
// its a strongs lookup
if (qry.substring(0, 1).toUpperCase() === 'H') {
const num = parseInt(qry.substr(1), 10);
const num = parseInt(qry.substring(1), 10);
for (let x = num; x < num + 10 && x < 8675; x++) {
words.push('H' + x);
}

View File

@ -207,14 +207,14 @@ export class StorageService extends SubscriberBase {
// console.log('Data saved to local store', data);
// update local
this.local.set(this.settingsPath, data).subscribe(
() => {
// nop
},
// error
() => {
this.appService.dispatchError(`Something went wrong and the Settings weren't saved. :(`);
}
this.addSubscription(
this.local.set(this.settingsPath, data).subscribe({
next: () => {},
complete: () => {},
error: () => {
this.appService.dispatchError(`Something went wrong and the Settings weren't saved. :(`);
},
})
);
// update remote