mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Angular 15 (#1764)
* Updated ngx-virtual-scroller * Removed the karma test config as it's breaking migration * Reverted to pre angular 15 * Upgraded packages and reverted target to ES6 for older devices * It's broken. Need to also find the safari version for old Ipads * Fixes some code in default pipe and many updates to packages. Removed support for old iOS versions as it restricted Kavita from using newer features. Build still broken. * More progress in getting build working on Angular 15. Removed polyfills.ts for new angular config * Remove all.css for icons and use scss instead * Removed stuff that isn't needed * Migrated extended linting to eslint, ran on project and updated issues. Removed a duplicate component that did nothing. Fixed a few places where lifecycle hooks werent being called as interface wasn't implemented. * App builds correctly. Source maps are still needed. * Fixed source maps and removed more testing stuff. I will re-add later in another release when I figure out how to properly tackle dependencies on backend. * Reverted back to old source map definition
This commit is contained in:
parent
12d0ae6f2c
commit
f64f232e51
16
UI/Web/.editorconfig
Normal file
16
UI/Web/.editorconfig
Normal file
@ -0,0 +1,16 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
51
UI/Web/.eslintrc.json
Normal file
51
UI/Web/.eslintrc.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"root": true,
|
||||
"ignorePatterns": [
|
||||
"projects/**/*"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.ts"
|
||||
],
|
||||
"parserOptions": {
|
||||
"project": [
|
||||
"tsconfig.json",
|
||||
"e2e/tsconfig.json"
|
||||
],
|
||||
"createDefaultProgram": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:@angular-eslint/recommended",
|
||||
"plugin:@angular-eslint/template/process-inline-templates"
|
||||
],
|
||||
"rules": {
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"prefix": "app",
|
||||
"style": "kebab-case",
|
||||
"type": "element"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"prefix": "app",
|
||||
"style": "camelCase",
|
||||
"type": "attribute"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.html"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@angular-eslint/template/recommended"
|
||||
],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"cookies": [],
|
||||
"origins": []
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"cli": {
|
||||
"analytics": "6b518972-3ce0-486d-bc55-740bf8308c77"
|
||||
"analytics": "6b518972-3ce0-486d-bc55-740bf8308c77",
|
||||
"schematicCollections": [
|
||||
"@angular-eslint/schematics"
|
||||
]
|
||||
},
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
@ -26,7 +29,10 @@
|
||||
"outputPath": "dist",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"polyfills": [
|
||||
"zone.js"
|
||||
],
|
||||
"inlineStyleLanguage": "scss",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"assets": [
|
||||
"src/assets",
|
||||
@ -37,11 +43,6 @@
|
||||
"output": "/assets/"
|
||||
}
|
||||
],
|
||||
"sourceMap": {
|
||||
"hidden": false,
|
||||
"scripts": true,
|
||||
"styles": true
|
||||
},
|
||||
"styles": [
|
||||
"src/styles.scss",
|
||||
"node_modules/@fortawesome/fontawesome-free/css/all.min.css"
|
||||
@ -51,6 +52,11 @@
|
||||
"node_modules/lazysizes/plugins/rias/ls.rias.min.js",
|
||||
"node_modules/lazysizes/plugins/attrchange/ls.attrchange.min.js"
|
||||
],
|
||||
"sourceMap": {
|
||||
"hidden": false,
|
||||
"scripts": true,
|
||||
"styles": true
|
||||
},
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
"buildOptimizer": false,
|
||||
@ -106,47 +112,14 @@
|
||||
"browserTarget": "kavita-webui:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"assets": [
|
||||
"src/assets",
|
||||
"src/site.webmanifest"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"builder": "@angular-eslint/builder:lint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"tsconfig.app.json",
|
||||
"tsconfig.spec.json",
|
||||
"e2e/tsconfig.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
"lintFilePatterns": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.html"
|
||||
]
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "e2e/protractor.conf.js",
|
||||
"devServerTarget": "kavita-webui:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "kavita-webui:serve:production"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,398 +0,0 @@
|
||||
// import { test, expect, Page } from '@playwright/test';
|
||||
|
||||
// test.beforeEach(async ({ page }) => {
|
||||
// await page.goto('https://demo.playwright.dev/todomvc');
|
||||
// });
|
||||
|
||||
// const TODO_ITEMS = [
|
||||
// 'buy some cheese',
|
||||
// 'feed the cat',
|
||||
// 'book a doctors appointment'
|
||||
// ];
|
||||
|
||||
// test.describe('New Todo', () => {
|
||||
// test('should allow me to add todo items', async ({ page }) => {
|
||||
// // Create 1st todo.
|
||||
// await page.locator('.new-todo').fill(TODO_ITEMS[0]);
|
||||
// await page.locator('.new-todo').press('Enter');
|
||||
|
||||
// // Make sure the list only has one todo item.
|
||||
// await expect(page.locator('.view label')).toHaveText([
|
||||
// TODO_ITEMS[0]
|
||||
// ]);
|
||||
|
||||
// // Create 2nd todo.
|
||||
// await page.locator('.new-todo').fill(TODO_ITEMS[1]);
|
||||
// await page.locator('.new-todo').press('Enter');
|
||||
|
||||
// // Make sure the list now has two todo items.
|
||||
// await expect(page.locator('.view label')).toHaveText([
|
||||
// TODO_ITEMS[0],
|
||||
// TODO_ITEMS[1]
|
||||
// ]);
|
||||
|
||||
// await checkNumberOfTodosInLocalStorage(page, 2);
|
||||
// });
|
||||
|
||||
// test('should clear text input field when an item is added', async ({ page }) => {
|
||||
// // Create one todo item.
|
||||
// await page.locator('.new-todo').fill(TODO_ITEMS[0]);
|
||||
// await page.locator('.new-todo').press('Enter');
|
||||
|
||||
// // Check that input is empty.
|
||||
// await expect(page.locator('.new-todo')).toBeEmpty();
|
||||
// await checkNumberOfTodosInLocalStorage(page, 1);
|
||||
// });
|
||||
|
||||
// test('should append new items to the bottom of the list', async ({ page }) => {
|
||||
// // Create 3 items.
|
||||
// await createDefaultTodos(page);
|
||||
|
||||
// // Check test using different methods.
|
||||
// await expect(page.locator('.todo-count')).toHaveText('3 items left');
|
||||
// await expect(page.locator('.todo-count')).toContainText('3');
|
||||
// await expect(page.locator('.todo-count')).toHaveText(/3/);
|
||||
|
||||
// // Check all items in one call.
|
||||
// await expect(page.locator('.view label')).toHaveText(TODO_ITEMS);
|
||||
// await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
// });
|
||||
|
||||
// test('should show #main and #footer when items added', async ({ page }) => {
|
||||
// await page.locator('.new-todo').fill(TODO_ITEMS[0]);
|
||||
// await page.locator('.new-todo').press('Enter');
|
||||
|
||||
// await expect(page.locator('.main')).toBeVisible();
|
||||
// await expect(page.locator('.footer')).toBeVisible();
|
||||
// await checkNumberOfTodosInLocalStorage(page, 1);
|
||||
// });
|
||||
// });
|
||||
|
||||
// test.describe('Mark all as completed', () => {
|
||||
// test.beforeEach(async ({ page }) => {
|
||||
// await createDefaultTodos(page);
|
||||
// await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
// });
|
||||
|
||||
// test.afterEach(async ({ page }) => {
|
||||
// await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
// });
|
||||
|
||||
// test('should allow me to mark all items as completed', async ({ page }) => {
|
||||
// // Complete all todos.
|
||||
// await page.locator('.toggle-all').check();
|
||||
|
||||
// // Ensure all todos have 'completed' class.
|
||||
// await expect(page.locator('.todo-list li')).toHaveClass(['completed', 'completed', 'completed']);
|
||||
// await checkNumberOfCompletedTodosInLocalStorage(page, 3);
|
||||
// });
|
||||
|
||||
// test('should allow me to clear the complete state of all items', async ({ page }) => {
|
||||
// // Check and then immediately uncheck.
|
||||
// await page.locator('.toggle-all').check();
|
||||
// await page.locator('.toggle-all').uncheck();
|
||||
|
||||
// // Should be no completed classes.
|
||||
// await expect(page.locator('.todo-list li')).toHaveClass(['', '', '']);
|
||||
// });
|
||||
|
||||
// test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
|
||||
// const toggleAll = page.locator('.toggle-all');
|
||||
// await toggleAll.check();
|
||||
// await expect(toggleAll).toBeChecked();
|
||||
// await checkNumberOfCompletedTodosInLocalStorage(page, 3);
|
||||
|
||||
// // Uncheck first todo.
|
||||
// const firstTodo = page.locator('.todo-list li').nth(0);
|
||||
// await firstTodo.locator('.toggle').uncheck();
|
||||
|
||||
// // Reuse toggleAll locator and make sure its not checked.
|
||||
// await expect(toggleAll).not.toBeChecked();
|
||||
|
||||
// await firstTodo.locator('.toggle').check();
|
||||
// await checkNumberOfCompletedTodosInLocalStorage(page, 3);
|
||||
|
||||
// // Assert the toggle all is checked again.
|
||||
// await expect(toggleAll).toBeChecked();
|
||||
// });
|
||||
// });
|
||||
|
||||
// test.describe('Item', () => {
|
||||
|
||||
// test('should allow me to mark items as complete', async ({ page }) => {
|
||||
// // Create two items.
|
||||
// for (const item of TODO_ITEMS.slice(0, 2)) {
|
||||
// await page.locator('.new-todo').fill(item);
|
||||
// await page.locator('.new-todo').press('Enter');
|
||||
// }
|
||||
|
||||
// // Check first item.
|
||||
// const firstTodo = page.locator('.todo-list li').nth(0);
|
||||
// await firstTodo.locator('.toggle').check();
|
||||
// await expect(firstTodo).toHaveClass('completed');
|
||||
|
||||
// // Check second item.
|
||||
// const secondTodo = page.locator('.todo-list li').nth(1);
|
||||
// await expect(secondTodo).not.toHaveClass('completed');
|
||||
// await secondTodo.locator('.toggle').check();
|
||||
|
||||
// // Assert completed class.
|
||||
// await expect(firstTodo).toHaveClass('completed');
|
||||
// await expect(secondTodo).toHaveClass('completed');
|
||||
// });
|
||||
|
||||
// test('should allow me to un-mark items as complete', async ({ page }) => {
|
||||
// // Create two items.
|
||||
// for (const item of TODO_ITEMS.slice(0, 2)) {
|
||||
// await page.locator('.new-todo').fill(item);
|
||||
// await page.locator('.new-todo').press('Enter');
|
||||
// }
|
||||
|
||||
// const firstTodo = page.locator('.todo-list li').nth(0);
|
||||
// const secondTodo = page.locator('.todo-list li').nth(1);
|
||||
// await firstTodo.locator('.toggle').check();
|
||||
// await expect(firstTodo).toHaveClass('completed');
|
||||
// await expect(secondTodo).not.toHaveClass('completed');
|
||||
// await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
|
||||
// await firstTodo.locator('.toggle').uncheck();
|
||||
// await expect(firstTodo).not.toHaveClass('completed');
|
||||
// await expect(secondTodo).not.toHaveClass('completed');
|
||||
// await checkNumberOfCompletedTodosInLocalStorage(page, 0);
|
||||
// });
|
||||
|
||||
// test('should allow me to edit an item', async ({ page }) => {
|
||||
// await createDefaultTodos(page);
|
||||
|
||||
// const todoItems = page.locator('.todo-list li');
|
||||
// const secondTodo = todoItems.nth(1);
|
||||
// await secondTodo.dblclick();
|
||||
// await expect(secondTodo.locator('.edit')).toHaveValue(TODO_ITEMS[1]);
|
||||
// await secondTodo.locator('.edit').fill('buy some sausages');
|
||||
// await secondTodo.locator('.edit').press('Enter');
|
||||
|
||||
// // Explicitly assert the new text value.
|
||||
// await expect(todoItems).toHaveText([
|
||||
// TODO_ITEMS[0],
|
||||
// 'buy some sausages',
|
||||
// TODO_ITEMS[2]
|
||||
// ]);
|
||||
// await checkTodosInLocalStorage(page, 'buy some sausages');
|
||||
// });
|
||||
// });
|
||||
|
||||
// test.describe('Editing', () => {
|
||||
// test.beforeEach(async ({ page }) => {
|
||||
// await createDefaultTodos(page);
|
||||
// await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
// });
|
||||
|
||||
// test('should hide other controls when editing', async ({ page }) => {
|
||||
// const todoItem = page.locator('.todo-list li').nth(1);
|
||||
// await todoItem.dblclick();
|
||||
// await expect(todoItem.locator('.toggle')).not.toBeVisible();
|
||||
// await expect(todoItem.locator('label')).not.toBeVisible();
|
||||
// await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
// });
|
||||
|
||||
// test('should save edits on blur', async ({ page }) => {
|
||||
// const todoItems = page.locator('.todo-list li');
|
||||
// await todoItems.nth(1).dblclick();
|
||||
// await todoItems.nth(1).locator('.edit').fill('buy some sausages');
|
||||
// await todoItems.nth(1).locator('.edit').dispatchEvent('blur');
|
||||
|
||||
// await expect(todoItems).toHaveText([
|
||||
// TODO_ITEMS[0],
|
||||
// 'buy some sausages',
|
||||
// TODO_ITEMS[2],
|
||||
// ]);
|
||||
// await checkTodosInLocalStorage(page, 'buy some sausages');
|
||||
// });
|
||||
|
||||
// test('should trim entered text', async ({ page }) => {
|
||||
// const todoItems = page.locator('.todo-list li');
|
||||
// await todoItems.nth(1).dblclick();
|
||||
// await todoItems.nth(1).locator('.edit').fill(' buy some sausages ');
|
||||
// await todoItems.nth(1).locator('.edit').press('Enter');
|
||||
|
||||
// await expect(todoItems).toHaveText([
|
||||
// TODO_ITEMS[0],
|
||||
// 'buy some sausages',
|
||||
// TODO_ITEMS[2],
|
||||
// ]);
|
||||
// await checkTodosInLocalStorage(page, 'buy some sausages');
|
||||
// });
|
||||
|
||||
// test('should remove the item if an empty text string was entered', async ({ page }) => {
|
||||
// const todoItems = page.locator('.todo-list li');
|
||||
// await todoItems.nth(1).dblclick();
|
||||
// await todoItems.nth(1).locator('.edit').fill('');
|
||||
// await todoItems.nth(1).locator('.edit').press('Enter');
|
||||
|
||||
// await expect(todoItems).toHaveText([
|
||||
// TODO_ITEMS[0],
|
||||
// TODO_ITEMS[2],
|
||||
// ]);
|
||||
// });
|
||||
|
||||
// test('should cancel edits on escape', async ({ page }) => {
|
||||
// const todoItems = page.locator('.todo-list li');
|
||||
// await todoItems.nth(1).dblclick();
|
||||
// await todoItems.nth(1).locator('.edit').press('Escape');
|
||||
// await expect(todoItems).toHaveText(TODO_ITEMS);
|
||||
// });
|
||||
// });
|
||||
|
||||
// test.describe('Counter', () => {
|
||||
// test('should display the current number of todo items', async ({ page }) => {
|
||||
// await page.locator('.new-todo').fill(TODO_ITEMS[0]);
|
||||
// await page.locator('.new-todo').press('Enter');
|
||||
// await expect(page.locator('.todo-count')).toContainText('1');
|
||||
|
||||
// await page.locator('.new-todo').fill(TODO_ITEMS[1]);
|
||||
// await page.locator('.new-todo').press('Enter');
|
||||
// await expect(page.locator('.todo-count')).toContainText('2');
|
||||
|
||||
// await checkNumberOfTodosInLocalStorage(page, 2);
|
||||
// });
|
||||
// });
|
||||
|
||||
// test.describe('Clear completed button', () => {
|
||||
// test.beforeEach(async ({ page }) => {
|
||||
// await createDefaultTodos(page);
|
||||
// });
|
||||
|
||||
// test('should display the correct text', async ({ page }) => {
|
||||
// await page.locator('.todo-list li .toggle').first().check();
|
||||
// await expect(page.locator('.clear-completed')).toHaveText('Clear completed');
|
||||
// });
|
||||
|
||||
// test('should remove completed items when clicked', async ({ page }) => {
|
||||
// const todoItems = page.locator('.todo-list li');
|
||||
// await todoItems.nth(1).locator('.toggle').check();
|
||||
// await page.locator('.clear-completed').click();
|
||||
// await expect(todoItems).toHaveCount(2);
|
||||
// await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
|
||||
// });
|
||||
|
||||
// test('should be hidden when there are no items that are completed', async ({ page }) => {
|
||||
// await page.locator('.todo-list li .toggle').first().check();
|
||||
// await page.locator('.clear-completed').click();
|
||||
// await expect(page.locator('.clear-completed')).toBeHidden();
|
||||
// });
|
||||
// });
|
||||
|
||||
// test.describe('Persistence', () => {
|
||||
// test('should persist its data', async ({ page }) => {
|
||||
// for (const item of TODO_ITEMS.slice(0, 2)) {
|
||||
// await page.locator('.new-todo').fill(item);
|
||||
// await page.locator('.new-todo').press('Enter');
|
||||
// }
|
||||
|
||||
// const todoItems = page.locator('.todo-list li');
|
||||
// await todoItems.nth(0).locator('.toggle').check();
|
||||
// await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
|
||||
// await expect(todoItems).toHaveClass(['completed', '']);
|
||||
|
||||
// // Ensure there is 1 completed item.
|
||||
// checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
|
||||
// // Now reload.
|
||||
// await page.reload();
|
||||
// await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
|
||||
// await expect(todoItems).toHaveClass(['completed', '']);
|
||||
// });
|
||||
// });
|
||||
|
||||
// test.describe('Routing', () => {
|
||||
// test.beforeEach(async ({ page }) => {
|
||||
// await createDefaultTodos(page);
|
||||
// // make sure the app had a chance to save updated todos in storage
|
||||
// // before navigating to a new view, otherwise the items can get lost :(
|
||||
// // in some frameworks like Durandal
|
||||
// await checkTodosInLocalStorage(page, TODO_ITEMS[0]);
|
||||
// });
|
||||
|
||||
// test('should allow me to display active items', async ({ page }) => {
|
||||
// await page.locator('.todo-list li .toggle').nth(1).check();
|
||||
// await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
// await page.locator('.filters >> text=Active').click();
|
||||
// await expect(page.locator('.todo-list li')).toHaveCount(2);
|
||||
// await expect(page.locator('.todo-list li')).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
|
||||
// });
|
||||
|
||||
// test('should respect the back button', async ({ page }) => {
|
||||
// await page.locator('.todo-list li .toggle').nth(1).check();
|
||||
// await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
|
||||
// await test.step('Showing all items', async () => {
|
||||
// await page.locator('.filters >> text=All').click();
|
||||
// await expect(page.locator('.todo-list li')).toHaveCount(3);
|
||||
// });
|
||||
|
||||
// await test.step('Showing active items', async () => {
|
||||
// await page.locator('.filters >> text=Active').click();
|
||||
// });
|
||||
|
||||
// await test.step('Showing completed items', async () => {
|
||||
// await page.locator('.filters >> text=Completed').click();
|
||||
// });
|
||||
|
||||
// await expect(page.locator('.todo-list li')).toHaveCount(1);
|
||||
// await page.goBack();
|
||||
// await expect(page.locator('.todo-list li')).toHaveCount(2);
|
||||
// await page.goBack();
|
||||
// await expect(page.locator('.todo-list li')).toHaveCount(3);
|
||||
// });
|
||||
|
||||
// test('should allow me to display completed items', async ({ page }) => {
|
||||
// await page.locator('.todo-list li .toggle').nth(1).check();
|
||||
// await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
// await page.locator('.filters >> text=Completed').click();
|
||||
// await expect(page.locator('.todo-list li')).toHaveCount(1);
|
||||
// });
|
||||
|
||||
// test('should allow me to display all items', async ({ page }) => {
|
||||
// await page.locator('.todo-list li .toggle').nth(1).check();
|
||||
// await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
// await page.locator('.filters >> text=Active').click();
|
||||
// await page.locator('.filters >> text=Completed').click();
|
||||
// await page.locator('.filters >> text=All').click();
|
||||
// await expect(page.locator('.todo-list li')).toHaveCount(3);
|
||||
// });
|
||||
|
||||
// test('should highlight the currently applied filter', async ({ page }) => {
|
||||
// await expect(page.locator('.filters >> text=All')).toHaveClass('selected');
|
||||
// await page.locator('.filters >> text=Active').click();
|
||||
// // Page change - active items.
|
||||
// await expect(page.locator('.filters >> text=Active')).toHaveClass('selected');
|
||||
// await page.locator('.filters >> text=Completed').click();
|
||||
// // Page change - completed items.
|
||||
// await expect(page.locator('.filters >> text=Completed')).toHaveClass('selected');
|
||||
// });
|
||||
// });
|
||||
|
||||
// async function createDefaultTodos(page: Page) {
|
||||
// for (const item of TODO_ITEMS) {
|
||||
// await page.locator('.new-todo').fill(item);
|
||||
// await page.locator('.new-todo').press('Enter');
|
||||
// }
|
||||
// }
|
||||
|
||||
// async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) {
|
||||
// return await page.waitForFunction(e => {
|
||||
// return JSON.parse(localStorage['react-todos']).length === e;
|
||||
// }, expected);
|
||||
// }
|
||||
|
||||
// async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) {
|
||||
// return await page.waitForFunction(e => {
|
||||
// return JSON.parse(localStorage['react-todos']).filter(i => i.completed).length === e;
|
||||
// }, expected);
|
||||
// }
|
||||
|
||||
// async function checkTodosInLocalStorage(page: Page, title: string) {
|
||||
// return await page.waitForFunction(t => {
|
||||
// return JSON.parse(localStorage['react-todos']).map(i => i.title).includes(t);
|
||||
// }, title);
|
||||
// }
|
@ -1,37 +0,0 @@
|
||||
// @ts-check
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
|
||||
|
||||
/**
|
||||
* @type { import("protractor").Config }
|
||||
*/
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
specs: [
|
||||
'./src/**/*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
browserName: 'chrome'
|
||||
},
|
||||
directConnect: true,
|
||||
SELENIUM_PROMISE_MANAGER: false,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 30000,
|
||||
print: function() {}
|
||||
},
|
||||
onPrepare() {
|
||||
require('ts-node').register({
|
||||
project: require('path').join(__dirname, './tsconfig.json')
|
||||
});
|
||||
jasmine.getEnv().addReporter(new SpecReporter({
|
||||
spec: {
|
||||
displayStacktrace: StacktraceOption.PRETTY
|
||||
}
|
||||
}));
|
||||
}
|
||||
};
|
@ -1,11 +0,0 @@
|
||||
import { browser, by, element } from 'protractor';
|
||||
|
||||
export class AppPage {
|
||||
async navigateTo(): Promise<unknown> {
|
||||
return browser.get(browser.baseUrl);
|
||||
}
|
||||
|
||||
async getTitleText(): Promise<string> {
|
||||
return element(by.css('app-root .content span')).getText();
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('When not authenticated, should be redirected to login page', async ({ page }) => {
|
||||
await page.goto('http://localhost:4200/', { waitUntil: 'networkidle' });
|
||||
expect(page.url()).toBe('http://localhost:4200/login');
|
||||
});
|
||||
|
||||
test('When not authenticated, should be redirected to login page from an authenticated page', async ({ page }) => {
|
||||
await page.goto('http://localhost:4200/library', { waitUntil: 'networkidle' });
|
||||
expect(page.url()).toBe('http://localhost:4200/login');
|
||||
});
|
||||
|
||||
// Not sure how to test when we need localStorage: https://github.com/microsoft/playwright/issues/6258
|
||||
// test('When authenticated, should be redirected to library page', async ({ page }) => {
|
||||
// await page.goto('http://localhost:4200/', { waitUntil: 'networkidle' });
|
||||
// console.log('url: ', page.url());
|
||||
// expect(page.url()).toBe('http://localhost:4200/library');
|
||||
// });
|
@ -1,43 +0,0 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test('When not authenticated, should be redirected to login page', async ({ page }) => {
|
||||
await page.goto('http://localhost:4200/', { waitUntil: 'networkidle' });
|
||||
expect(page.url()).toBe('http://localhost:4200/login');
|
||||
});
|
||||
|
||||
test('Should be able to log in', async ({ page }) => {
|
||||
|
||||
await page.goto('http://localhost:4200/login', { waitUntil: 'networkidle' });
|
||||
const username = page.locator('#username');
|
||||
expect(username).toBeEditable();
|
||||
const password = page.locator('#password');
|
||||
expect(password).toBeEditable();
|
||||
|
||||
await username.type('Joe');
|
||||
await password.type('P4ssword');
|
||||
|
||||
const button = page.locator('button[type="submit"]');
|
||||
await button.click();
|
||||
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForTimeout(200);
|
||||
expect(page.url()).toBe('http://localhost:4200/library');
|
||||
});
|
||||
|
||||
test('Should get a toastr when no username', async ({ page }) => {
|
||||
|
||||
await page.goto('http://localhost:4200/login', { waitUntil: 'networkidle' });
|
||||
const username = page.locator('#username');
|
||||
expect(username).toBeEditable();
|
||||
|
||||
await username.type('');
|
||||
|
||||
const button = page.locator('button[type="submit"]');
|
||||
await button.click();
|
||||
|
||||
await page.waitForTimeout(100);
|
||||
const toastr = page.locator('#toast-container div[role="alertdialog"]')
|
||||
await expect(toastr).toHaveText('Invalid username');
|
||||
|
||||
expect(page.url()).toBe('http://localhost:4200/login');
|
||||
});
|
@ -1,34 +0,0 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test('When on login page, clicking Forgot Password should redirect', async ({ page }) => {
|
||||
await page.goto('http://localhost:4200/login', { waitUntil: 'networkidle' });
|
||||
|
||||
await page.click('a[routerlink="/registration/reset-password"]')
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
expect(page.url()).toBe('http://localhost:4200/registration/reset-password');
|
||||
});
|
||||
|
||||
test('Going directly to reset url should stay on the page', async ({page}) => {
|
||||
await page.goto('http://localhost:4200/registration/reset-password', { waitUntil: 'networkidle' });
|
||||
const email = page.locator('#email');
|
||||
expect(email).toBeEditable();
|
||||
})
|
||||
|
||||
test('Submitting an email, should give a prompt to user, redirect back to login', async ({ page }) => {
|
||||
await page.goto('http://localhost:4200/registration/reset-password', { waitUntil: 'networkidle' });
|
||||
|
||||
const email = page.locator('#email');
|
||||
expect(email).toBeEditable();
|
||||
|
||||
await email.type('XXX@gmail.com');
|
||||
|
||||
const button = page.locator('button[type="submit"]');
|
||||
await button.click();
|
||||
|
||||
const toastr = page.locator('#toast-container div[role="alertdialog"]')
|
||||
await expect(toastr).toHaveText('An email will be sent to the email if it exists in our database');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
expect(page.url()).toBe('http://localhost:4200/login');
|
||||
});
|
@ -1,13 +0,0 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test.use({ storageState: 'storage/admin.json' });
|
||||
|
||||
test('When on login page, side nav should not render', async ({ page }) => {
|
||||
await page.goto('http://localhost:4200/login', { waitUntil: 'networkidle' });
|
||||
await expect(page.locator(".side-nav")).toHaveCount(0)
|
||||
});
|
||||
|
||||
test('When on library page, side nav should render', async ({ page }) => {
|
||||
await page.goto('http://localhost:4200/library', { waitUntil: 'networkidle' });
|
||||
await expect(page.locator(".side-nav")).toHaveCount(1)
|
||||
});
|
@ -1,13 +0,0 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/e2e",
|
||||
"module": "commonjs",
|
||||
"target": "es2020",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
import { Browser, chromium, FullConfig, request } from '@playwright/test';
|
||||
|
||||
async function globalSetup(config: FullConfig) {
|
||||
let requestContext = await request.newContext();
|
||||
var token = await requestContext.post('http://localhost:5000/account/login', {
|
||||
form: {
|
||||
'user': 'Joe',
|
||||
'password': 'P4ssword'
|
||||
}
|
||||
});
|
||||
//console.log(token.json());
|
||||
// Save signed-in state to 'storageState.json'.
|
||||
//await requestContext.storageState({ path: 'adminStorageState.json' });
|
||||
await requestContext.dispose();
|
||||
|
||||
requestContext = await request.newContext();
|
||||
await requestContext.post('http://localhost:5000/account/login', {
|
||||
form: {
|
||||
'user': 'nonadmin',
|
||||
'password': 'P4ssword'
|
||||
}
|
||||
});
|
||||
// Save signed-in state to 'storageState.json'.
|
||||
//await requestContext.storageState({ path: 'nonAdminStorageState.json' });
|
||||
await requestContext.dispose();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// async function globalSetup (config: FullConfig) {
|
||||
// const browser = await chromium.launch()
|
||||
// await saveStorage(browser, 'nonadmin', 'P4ssword', 'storage/user.json')
|
||||
// await saveStorage(browser, 'Joe', 'P4ssword', 'storage/admin.json')
|
||||
// await browser.close()
|
||||
// }
|
||||
|
||||
async function saveStorage (browser: Browser, username: string, password: string, saveStoragePath: string) {
|
||||
const page = await browser.newPage()
|
||||
await page.goto('http://localhost:5000/account/login')
|
||||
await page.type('#username', username)
|
||||
await page.type('#password', password)
|
||||
await page.click('button[type="submit"]')
|
||||
await page.context().storageState({ path: saveStoragePath })
|
||||
}
|
||||
|
||||
export default globalSetup;
|
Binary file not shown.
@ -1,4 +0,0 @@
|
||||
{
|
||||
"cookies": [],
|
||||
"origins": []
|
||||
}
|
13863
UI/Web/package-lock.json
generated
13863
UI/Web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -15,75 +15,66 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular-slider/ngx-slider": "^2.0.3",
|
||||
"@angular/animations": "^14.1.1",
|
||||
"@angular/cdk": "^13.2.2",
|
||||
"@angular/common": "^14.1.1",
|
||||
"@angular/compiler": "^14.1.1",
|
||||
"@angular/core": "^14.1.1",
|
||||
"@angular/forms": "^14.1.1",
|
||||
"@angular/localize": "^14.1.1",
|
||||
"@angular/platform-browser": "^14.1.1",
|
||||
"@angular/platform-browser-dynamic": "^14.1.1",
|
||||
"@angular/router": "^14.1.1",
|
||||
"@fortawesome/fontawesome-free": "^6.0.0",
|
||||
"@iharbeck/ngx-virtual-scroller": "^13.0.4",
|
||||
"@microsoft/signalr": "^6.0.2",
|
||||
"@ng-bootstrap/ng-bootstrap": "^13.0.0",
|
||||
"@popperjs/core": "^2.11.2",
|
||||
"@swimlane/ngx-charts": "^20.1.0",
|
||||
"@angular/animations": "^15.1.2",
|
||||
"@angular/cdk": "^15.1.2",
|
||||
"@angular/common": "^15.1.2",
|
||||
"@angular/compiler": "^15.1.2",
|
||||
"@angular/core": "^15.1.2",
|
||||
"@angular/forms": "^15.1.2",
|
||||
"@angular/localize": "^15.1.2",
|
||||
"@angular/platform-browser": "^15.1.2",
|
||||
"@angular/platform-browser-dynamic": "^15.1.2",
|
||||
"@angular/router": "^15.1.2",
|
||||
"@fortawesome/fontawesome-free": "^6.2.0",
|
||||
"@iharbeck/ngx-virtual-scroller": "^15.0.0",
|
||||
"@microsoft/signalr": "^7.0.2",
|
||||
"@ng-bootstrap/ng-bootstrap": "^14.0.1",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"@swimlane/ngx-charts": "^20.1.2",
|
||||
"@tweenjs/tween.js": "^18.6.4",
|
||||
"@types/file-saver": "^2.0.5",
|
||||
"bootstrap": "^5.2.0",
|
||||
"bowser": "^2.11.0",
|
||||
"bootstrap": "^5.2.3",
|
||||
"browser": "^0.2.6",
|
||||
"eventsource": "^2.0.2",
|
||||
"file-saver": "^2.0.5",
|
||||
"lazysizes": "^5.3.2",
|
||||
"ng-circle-progress": "^1.6.0",
|
||||
"ng-circle-progress": "^1.7.1",
|
||||
"ng-swipe": "^2.0.1",
|
||||
"ngx-color-picker": "^12.0.0",
|
||||
"ngx-extended-pdf-viewer": "^15.0.0",
|
||||
"ngx-file-drop": "^14.0.1",
|
||||
"ngx-toastr": "^14.2.1",
|
||||
"ngx-color-picker": "^13.0.0",
|
||||
"ngx-extended-pdf-viewer": "^15.2.2",
|
||||
"ngx-file-drop": "^14.0.2",
|
||||
"ngx-slider-v2": "^15.0.3",
|
||||
"ngx-toastr": "^16.0.2",
|
||||
"requires": "^1.0.2",
|
||||
"rxjs": "~7.5.4",
|
||||
"rxjs": "^7.8.0",
|
||||
"screenfull": "^6.0.2",
|
||||
"swiper": "^8.4.4",
|
||||
"tslib": "^2.3.1",
|
||||
"webpack-bundle-analyzer": "^4.5.0",
|
||||
"zone.js": "~0.11.4"
|
||||
"swiper": "^8.4.6",
|
||||
"tslib": "^2.3.0",
|
||||
"webpack-bundle-analyzer": "^4.7.0",
|
||||
"zone.js": "~0.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^14.1.1",
|
||||
"@angular/cli": "^14.1.1",
|
||||
"@angular/compiler-cli": "^14.1.1",
|
||||
"@playwright/test": "^1.23.2",
|
||||
"@angular-devkit/build-angular": "^15.1.3",
|
||||
"@angular-eslint/builder": "15.2.0",
|
||||
"@angular-eslint/eslint-plugin": "15.2.0",
|
||||
"@angular-eslint/eslint-plugin-template": "15.2.0",
|
||||
"@angular-eslint/schematics": "15.2.0",
|
||||
"@angular-eslint/template-parser": "15.2.0",
|
||||
"@angular/cli": "^15.1.3",
|
||||
"@angular/compiler-cli": "^15.1.2",
|
||||
"@playwright/test": "^1.30.0",
|
||||
"@types/d3": "^7.4.0",
|
||||
"@types/jest": "^27.4.0",
|
||||
"@types/node": "^17.0.17",
|
||||
"codelyzer": "^6.0.2",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^17.0.45",
|
||||
"@typescript-eslint/eslint-plugin": "5.48.1",
|
||||
"@typescript-eslint/parser": "5.48.1",
|
||||
"ajv": "^7.2.4",
|
||||
"eslint": "^8.31.0",
|
||||
"jest": "^27.5.1",
|
||||
"jest-preset-angular": "^11.1.0",
|
||||
"jest-preset-angular": "^11.1.2",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"playwright": "^1.24.2",
|
||||
"protractor": "~7.0.0",
|
||||
"playwright": "^1.30.0",
|
||||
"ts-node": "~10.5.0",
|
||||
"tslint": "^6.1.3",
|
||||
"typescript": "~4.7.4"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "jest-preset-angular",
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/setupJest.ts"
|
||||
],
|
||||
"testPathIgnorePatterns": [
|
||||
"<rootDir>/node_modules/",
|
||||
"<rootDir>/dist/"
|
||||
],
|
||||
"globals": {
|
||||
"ts-jest": {
|
||||
"tsConfig": "<rootDir>/tsconfig.spec.json",
|
||||
"stringifyContentPathRegex": "\\.html$"
|
||||
}
|
||||
}
|
||||
"typescript": "~4.9.4"
|
||||
}
|
||||
}
|
||||
|
@ -1,106 +0,0 @@
|
||||
import type { PlaywrightTestConfig } from '@playwright/test';
|
||||
import { devices } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
// require('dotenv').config();
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
const config: PlaywrightTestConfig = {
|
||||
testDir: './e2e',
|
||||
/* Maximum time one test can run for. */
|
||||
timeout: 30 * 1000,
|
||||
expect: {
|
||||
/**
|
||||
* Maximum time expect() should wait for the condition to be met.
|
||||
* For example in `await expect(locator).toHaveText();`
|
||||
*/
|
||||
timeout: 5000
|
||||
},
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: 'html',
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
||||
actionTimeout: 0,
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
baseURL: 'http://localhost:4200',
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
},
|
||||
globalSetup: require.resolve('./global-setup'),
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
},
|
||||
},
|
||||
|
||||
// {
|
||||
// name: 'firefox',
|
||||
// use: {
|
||||
// ...devices['Desktop Firefox'],
|
||||
// },
|
||||
// },
|
||||
|
||||
// {
|
||||
// name: 'webkit',
|
||||
// use: {
|
||||
// ...devices['Desktop Safari'],
|
||||
// },
|
||||
// },
|
||||
|
||||
/* Test against mobile viewports. */
|
||||
// {
|
||||
// name: 'Mobile Chrome',
|
||||
// use: {
|
||||
// ...devices['Pixel 5'],
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile Safari',
|
||||
// use: {
|
||||
// ...devices['iPhone 12'],
|
||||
// },
|
||||
// },
|
||||
|
||||
/* Test against branded browsers. */
|
||||
// {
|
||||
// name: 'Microsoft Edge',
|
||||
// use: {
|
||||
// channel: 'msedge',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'Google Chrome',
|
||||
// use: {
|
||||
// channel: 'chrome',
|
||||
// },
|
||||
// },
|
||||
],
|
||||
|
||||
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
|
||||
// outputDir: 'test-results/',
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
// webServer: {
|
||||
// command: 'npm run start',
|
||||
// port: 3000,
|
||||
// },
|
||||
};
|
||||
|
||||
export default config;
|
@ -1,19 +0,0 @@
|
||||
import 'jest-preset-angular';
|
||||
|
||||
|
||||
/* global mocks for jsdom */
|
||||
const mock = () => {
|
||||
let storage: { [key: string]: string } = {};
|
||||
return {
|
||||
getItem: (key: string) => (key in storage ? storage[key] : null),
|
||||
setItem: (key: string, value: string) => (storage[key] = value || ''),
|
||||
removeItem: (key: string) => delete storage[key],
|
||||
clear: () => (storage = {})
|
||||
};
|
||||
};
|
||||
|
||||
Object.defineProperty(window, 'localStorage', { value: mock() });
|
||||
Object.defineProperty(window, 'sessionStorage', { value: mock() });
|
||||
Object.defineProperty(window, 'getComputedStyle', {
|
||||
value: () => ['-webkit-appearance'],
|
||||
});
|
@ -1 +0,0 @@
|
||||
<p>devices works!</p>
|
@ -1,15 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-devices',
|
||||
templateUrl: './devices.component.html',
|
||||
styleUrls: ['./devices.component.scss']
|
||||
})
|
||||
export class DevicesComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { FormGroup, FormControl, Validators } from '@angular/forms';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { Member } from 'src/app/_models/auth/member';
|
||||
@ -9,7 +9,7 @@ import { AccountService } from 'src/app/_services/account.service';
|
||||
templateUrl: './reset-password-modal.component.html',
|
||||
styleUrls: ['./reset-password-modal.component.scss']
|
||||
})
|
||||
export class ResetPasswordModalComponent implements OnInit {
|
||||
export class ResetPasswordModalComponent {
|
||||
|
||||
@Input() member!: Member;
|
||||
errorMessage = '';
|
||||
@ -19,9 +19,6 @@ export class ResetPasswordModalComponent implements OnInit {
|
||||
|
||||
constructor(public modal: NgbActiveModal, private accountService: AccountService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
save() {
|
||||
this.accountService.resetPassword(this.member.username, this.resetPasswordForm.value.password,'').subscribe(() => {
|
||||
this.modal.close();
|
||||
|
@ -19,7 +19,7 @@
|
||||
<div>Shared Folders: {{library.folders.length + ' folders'}}</div>
|
||||
<div>
|
||||
Last Scanned:
|
||||
<span *ngIf="library.lastScanned == '0001-01-01T00:00:00'; else activeDate">Never</span>
|
||||
<span *ngIf="library.lastScanned === '0001-01-01T00:00:00'; else activeDate">Never</span>
|
||||
<ng-template #activeDate>
|
||||
{{library.lastScanned | timeAgo}}
|
||||
</ng-template>
|
||||
|
@ -53,7 +53,7 @@
|
||||
</h4>
|
||||
<div class="user-info">
|
||||
<div>Last Active:
|
||||
<span *ngIf="member.lastActive == '0001-01-01T00:00:00'; else activeDate">Never</span>
|
||||
<span *ngIf="member.lastActive === '0001-01-01T00:00:00'; else activeDate">Never</span>
|
||||
<ng-template #activeDate>
|
||||
{{member.lastActive | date: 'short'}}
|
||||
</ng-template>
|
||||
|
@ -1,15 +1,12 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-announcements',
|
||||
templateUrl: './announcements.component.html',
|
||||
styleUrls: ['./announcements.component.scss']
|
||||
})
|
||||
export class AnnouncementsComponent implements OnInit {
|
||||
export class AnnouncementsComponent {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,9 +3,9 @@
|
||||
<div [ngClass]="{'closed' : (navService.sideNavCollapsed$ | async), 'content-wrapper': navService.sideNavVisibility$ | async}">
|
||||
<a id="content"></a>
|
||||
<app-side-nav *ngIf="navService.sideNavVisibility$ | async as sideNavVisibile"></app-side-nav>
|
||||
<div class="container-fluid" [ngClass]="{'g-0': !(navService.sideNavVisibility$ | async)}">
|
||||
<div class="container-fluid" [ngClass]="{'g-0': (navService.sideNavVisibility$ | async) === false}">
|
||||
<div style="padding: 20px 0 0;" *ngIf="navService.sideNavVisibility$ | async else noSideNav">
|
||||
<div class="companion-bar" [ngClass]="{'companion-bar-content': !(navService.sideNavCollapsed$ | async)}">
|
||||
<div class="companion-bar" [ngClass]="{'companion-bar-content': (navService.sideNavCollapsed$ | async) === false}">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Component, HostListener, Inject, OnInit } from '@angular/core';
|
||||
import { NavigationStart, Router } from '@angular/router';
|
||||
import { distinctUntilChanged, map, take } from 'rxjs/operators';
|
||||
import { map, take } from 'rxjs/operators';
|
||||
import { AccountService } from './_services/account.service';
|
||||
import { LibraryService } from './_services/library.service';
|
||||
import { MessageHubService } from './_services/message-hub.service';
|
||||
@ -8,7 +8,6 @@ import { NavService } from './_services/nav.service';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { NgbModal, NgbRatingConfig } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { DeviceService } from './_services/device.service';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
|
@ -12,7 +12,6 @@ import { ErrorInterceptor } from './_interceptors/error.interceptor';
|
||||
import { SAVER, getSaver } from './shared/_providers/saver.provider';
|
||||
import { SidenavModule } from './sidenav/sidenav.module';
|
||||
import { NavModule } from './nav/nav.module';
|
||||
import { DevicesComponent } from './_components/devices/devices.component';
|
||||
|
||||
|
||||
|
||||
@ -24,7 +23,6 @@ if (disableAnimations) console.error("Web Animations have been disabled as your
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
DevicesComponent,
|
||||
],
|
||||
imports: [
|
||||
HttpClientModule,
|
||||
|
@ -11,7 +11,7 @@
|
||||
</div>
|
||||
<ng-template #nestedChildren>
|
||||
<ul *ngFor="let chapterGroup of chapters" class="chapter-title">
|
||||
<li class="{{chapterGroup.page == pageNum ? 'active': ''}}" (click)="loadChapterPage(chapterGroup.page, '')">
|
||||
<li class="{{chapterGroup.page === pageNum ? 'active': ''}}" (click)="loadChapterPage(chapterGroup.page, '')">
|
||||
{{chapterGroup.title}}
|
||||
</li>
|
||||
<ul *ngFor="let chapter of chapterGroup.children">
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { BookChapterItem } from '../../_models/book-chapter-item';
|
||||
|
||||
@ -8,7 +8,7 @@ import { BookChapterItem } from '../../_models/book-chapter-item';
|
||||
styleUrls: ['./table-of-contents.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.Default
|
||||
})
|
||||
export class TableOfContentsComponent implements OnInit, OnDestroy {
|
||||
export class TableOfContentsComponent implements OnDestroy {
|
||||
|
||||
@Input() chapterId!: number;
|
||||
@Input() pageNum!: number;
|
||||
@ -23,9 +23,6 @@ export class TableOfContentsComponent implements OnInit, OnDestroy {
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.onDestroy.next();
|
||||
this.onDestroy.complete();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { FormGroup, FormControl } from '@angular/forms';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
@ -13,7 +13,7 @@ import { CollectionTagService } from 'src/app/_services/collection-tag.service';
|
||||
styleUrls: ['./bulk-add-to-collection.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class BulkAddToCollectionComponent implements OnInit {
|
||||
export class BulkAddToCollectionComponent implements OnInit, AfterViewInit {
|
||||
|
||||
@Input() title!: string;
|
||||
/**
|
||||
|
@ -177,9 +177,6 @@ export class BulkSelectionService {
|
||||
|
||||
action.children = action.children.filter((childAction) => this.applyFilter(childAction, allowedActions));
|
||||
|
||||
// action.children?.forEach((childAction) => {
|
||||
// this.applyFilter(childAction, allowedActions);
|
||||
// });
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
<li [ngbNavItem]="tabs[TabID.Cover]" [disabled]="!(isAdmin$ | async)">
|
||||
<li [ngbNavItem]="tabs[TabID.Cover]" [disabled]="(isAdmin$ | async) === false">
|
||||
<a ngbNavLink>{{tabs[TabID.Cover].title}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<app-cover-image-chooser [(imageUrls)]="imageUrls"
|
||||
@ -108,7 +108,7 @@
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
<li [ngbNavItem]="tabs[TabID.Files]" [disabled]="!(isAdmin$ | async)">
|
||||
<li [ngbNavItem]="tabs[TabID.Files]" [disabled]="(isAdmin$ | async) === false">
|
||||
<a ngbNavLink>{{tabs[TabID.Files].title}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<h4 *ngIf="!utilityService.isChapter(data)">{{utilityService.formatChapterName(libraryType) + 's'}}</h4>
|
||||
@ -145,7 +145,7 @@
|
||||
<div class="col" *ngIf="data.hasOwnProperty('created')">
|
||||
Added:
|
||||
<!-- TODO: This data.created can be removed after v0.5.5 release -->
|
||||
<ng-container *ngIf="file.created == '0001-01-01T00:00:00'; else fileDate">
|
||||
<ng-container *ngIf="file.created === '0001-01-01T00:00:00'; else fileDate">
|
||||
{{data.created | date: 'short' | defaultDate}}
|
||||
</ng-container>
|
||||
<ng-template #fileDate>
|
||||
|
@ -7,7 +7,7 @@
|
||||
</span>
|
||||
<span *ngIf="header !== undefined && header.length > 0">
|
||||
{{header}}
|
||||
<span class="badge bg-primary rounded-pill" attr.aria-label="{{pagination.totalItems}} total items" *ngIf="pagination != undefined">{{pagination.totalItems}}</span>
|
||||
<span class="badge bg-primary rounded-pill" attr.aria-label="{{pagination.totalItems}} total items" *ngIf="pagination !== undefined">{{pagination.totalItems}}</span>
|
||||
</span>
|
||||
</h2>
|
||||
</div>
|
||||
|
@ -9,9 +9,9 @@
|
||||
<ng-template #submenu let-list="list">
|
||||
<ng-container *ngFor="let action of list">
|
||||
<!-- Non Submenu items -->
|
||||
<ng-container *ngIf="action.children === undefined || action?.children?.length === 0 || action.dynamicList != undefined ; else submenuDropdown">
|
||||
<ng-container *ngIf="action.children === undefined || action?.children?.length === 0 || action.dynamicList !== undefined ; else submenuDropdown">
|
||||
|
||||
<ng-container *ngIf="action.dynamicList != undefined && (action.dynamicList | async | dynamicList) as dList; else justItem">
|
||||
<ng-container *ngIf="action.dynamicList !== undefined && (action.dynamicList | async | dynamicList) as dList; else justItem">
|
||||
<ng-container *ngFor="let dynamicItem of dList">
|
||||
<button ngbDropdownItem (click)="performDynamicClick($event, action, dynamicItem)">{{dynamicItem.title}}</button>
|
||||
</ng-container>
|
||||
|
@ -41,7 +41,7 @@
|
||||
<span class="visually-hidden">(promoted)</span>
|
||||
</span>
|
||||
<ng-container *ngIf="format | mangaFormat as formatString">
|
||||
<i class="fa {{format | mangaFormatIcon}} me-1" aria-hidden="true" *ngIf="format != MangaFormat.UNKNOWN" title="{{formatString}}"></i>
|
||||
<i class="fa {{format | mangaFormatIcon}} me-1" aria-hidden="true" *ngIf="format !== MangaFormat.UNKNOWN" title="{{formatString}}"></i>
|
||||
<span class="visually-hidden">{{formatString}}</span>
|
||||
</ng-container>
|
||||
{{title}}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { ChapterMetadata } from 'src/app/_models/metadata/chapter-metadata';
|
||||
|
||||
@Component({
|
||||
@ -7,10 +7,8 @@ import { ChapterMetadata } from 'src/app/_models/metadata/chapter-metadata';
|
||||
styleUrls: ['./chapter-metadata-detail.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ChapterMetadataDetailComponent implements OnInit {
|
||||
export class ChapterMetadataDetailComponent {
|
||||
@Input() chapter: ChapterMetadata | undefined;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Download } from 'src/app/shared/_models/download';
|
||||
import { DownloadEvent } from 'src/app/shared/_services/download.service';
|
||||
@ -9,16 +9,12 @@ import { DownloadEvent } from 'src/app/shared/_services/download.service';
|
||||
styleUrls: ['./download-indicator.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class DownloadIndicatorComponent implements OnInit {
|
||||
export class DownloadIndicatorComponent {
|
||||
|
||||
/**
|
||||
* Observable that represents when the download completes
|
||||
*/
|
||||
@Input() download$!: Observable<Download | DownloadEvent | null> | null;
|
||||
|
||||
constructor(private readonly cdRef: ChangeDetectorRef) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
constructor() { }
|
||||
}
|
||||
|
@ -1,26 +1,26 @@
|
||||
<ng-container [ngSwitch]="libraryType">
|
||||
<ng-container *ngSwitchCase="LibraryType.Comic">
|
||||
<ng-container *ngIf="titleName != '' && prioritizeTitleName; else fullComicTitle">
|
||||
<ng-container *ngIf="titleName !== '' && prioritizeTitleName; else fullComicTitle">
|
||||
{{titleName}}
|
||||
</ng-container>
|
||||
<ng-template #fullComicTitle>
|
||||
{{seriesName.length > 0 ? seriesName + ' - ' : ''}}
|
||||
<ng-container *ngIf="includeVolume && volumeTitle != ''">
|
||||
{{entity.number != 0 ? (isChapter && includeVolume ? volumeTitle : '') : ''}}
|
||||
<ng-container *ngIf="includeVolume && volumeTitle !== ''">
|
||||
{{entity.number !== 0 ? (isChapter && includeVolume ? volumeTitle : '') : ''}}
|
||||
</ng-container>
|
||||
{{entity.number != 0 ? (isChapter ? 'Issue #' + entity.number : volumeTitle) : 'Special'}}
|
||||
{{entity.number !== 0 ? (isChapter ? 'Issue #' + entity.number : volumeTitle) : 'Special'}}
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="LibraryType.Manga">
|
||||
<ng-container *ngIf="titleName != '' && prioritizeTitleName; else fullMangaTitle">
|
||||
<ng-container *ngIf="titleName !== '' && prioritizeTitleName; else fullMangaTitle">
|
||||
{{titleName}}
|
||||
</ng-container>
|
||||
<ng-template #fullMangaTitle>
|
||||
{{seriesName.length > 0 ? seriesName + ' - ' : ''}}
|
||||
<ng-container *ngIf="includeVolume && volumeTitle != ''">
|
||||
{{entity.number != 0 ? (isChapter && includeVolume ? volumeTitle : '') : ''}}
|
||||
<ng-container *ngIf="includeVolume && volumeTitle !== ''">
|
||||
{{entity.number !== 0 ? (isChapter && includeVolume ? volumeTitle : '') : ''}}
|
||||
</ng-container>
|
||||
{{entity.number != 0 ? (isChapter ? 'Chapter ' + entity.number : volumeTitle) : 'Special'}}
|
||||
{{entity.number !== 0 ? (isChapter ? 'Chapter ' + entity.number : volumeTitle) : 'Special'}}
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="LibraryType.Book">
|
||||
|
@ -22,7 +22,7 @@
|
||||
</button>
|
||||
</h5>
|
||||
<!-- This isn't perfect, but it might work. TODO: Polish this-->
|
||||
<h6 class="text-muted" [ngClass]="{'subtitle-with-actionables' : actions.length > 0}" *ngIf="Title != '' && showTitle">{{Title}}</h6>
|
||||
<h6 class="text-muted" [ngClass]="{'subtitle-with-actionables' : actions.length > 0}" *ngIf="Title !== '' && showTitle">{{Title}}</h6>
|
||||
<ng-container *ngIf="summary.length > 0">
|
||||
<div class="mt-2 ps-2">
|
||||
<app-read-more [text]="summary" [blur]="pagesRead === 0 && blur" [maxLength]="250"></app-read-more>
|
||||
|
@ -19,7 +19,7 @@ import { ManagaReaderService } from '../../_series/managa-reader.service';
|
||||
styleUrls: ['./double-no-cover-renderer.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class DoubleNoCoverRendererComponent implements OnInit {
|
||||
export class DoubleNoCoverRendererComponent implements OnInit, OnDestroy {
|
||||
|
||||
@Input() readerSettings$!: Observable<ReaderSetting>;
|
||||
@Input() image$!: Observable<HTMLImageElement | null>;
|
||||
|
@ -117,7 +117,7 @@
|
||||
|
||||
<div class="fixed-bottom overlay" *ngIf="menuOpen" [@slideFromBottom]="menuOpen">
|
||||
|
||||
<div class="mb-3" *ngIf="pageOptions != undefined && pageOptions.ceil != undefined">
|
||||
<div class="mb-3" *ngIf="pageOptions !== undefined && pageOptions.ceil !== undefined">
|
||||
<span class="visually-hidden" id="slider-info"></span>
|
||||
<div class="row g-0">
|
||||
<button class="btn btn-icon col-1" [disabled]="prevChapterDisabled" (click)="loadPrevChapter();resetMenuCloseTimer();" title="Prev Chapter/Volume"><i class="fa fa-fast-backward" aria-hidden="true"></i></button>
|
||||
|
@ -2,7 +2,7 @@ import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, E
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BehaviorSubject, debounceTime, distinctUntilChanged, forkJoin, fromEvent, map, merge, Observable, ReplaySubject, Subject, take, takeUntil, tap } from 'rxjs';
|
||||
import { LabelType, ChangeContext, Options } from '@angular-slider/ngx-slider';
|
||||
import { LabelType, ChangeContext, Options } from 'ngx-slider-v2';
|
||||
import { trigger, state, style, transition, animate } from '@angular/animations';
|
||||
import { FormGroup, FormBuilder, FormControl } from '@angular/forms';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
@ -4,7 +4,7 @@ import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { MangaReaderRoutingModule } from './manga-reader.router.module';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { NgxSliderModule } from '@angular-slider/ngx-slider';
|
||||
import { NgxSliderModule } from 'ngx-slider-v2';
|
||||
import { InfiniteScrollerComponent } from './_components/infinite-scroller/infinite-scroller.component';
|
||||
import { ReaderSharedModule } from '../reader-shared/reader-shared.module';
|
||||
import { PipeModule } from '../pipe/pipe.module';
|
||||
|
@ -1,5 +1,5 @@
|
||||
<form [formGroup]="typeaheadForm" class="grouped-typeahead">
|
||||
<div class="typeahead-input" [ngClass]="{'focused': hasFocus == true}" (click)="onInputFocus($event)">
|
||||
<div class="typeahead-input" [ngClass]="{'focused': hasFocus}" (click)="onInputFocus($event)">
|
||||
<div class="search">
|
||||
<input #input [id]="id" type="text" autocomplete="off" formControlName="typeahead" [placeholder]="placeholder"
|
||||
aria-haspopup="listbox" aria-owns="dropdown" aria-expanded="hasFocus && (grouppedData.persons.length || grouppedData.collections.length || grouppedData.series.length || grouppedData.persons.length || grouppedData.tags.length || grouppedData.genres.length)"
|
||||
|
@ -6,7 +6,7 @@ import { Pipe, PipeTransform } from '@angular/core';
|
||||
export class DefaultValuePipe implements PipeTransform {
|
||||
|
||||
transform(value: any, replacementString = '—'): string {
|
||||
if (value === null || value === undefined || value === '' || value === Infinity || value === NaN) return replacementString;
|
||||
if (value === null || value === undefined || value === '' || value === Infinity || Number.isNaN(value)) return replacementString;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, EventEmitter, Input, OnInit, Output, TemplateRef } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, EventEmitter, Input, Output, TemplateRef } from '@angular/core';
|
||||
|
||||
export interface IndexUpdateEvent {
|
||||
fromPosition: number;
|
||||
@ -18,7 +18,7 @@ export interface ItemRemoveEvent {
|
||||
styleUrls: ['./draggable-ordered-list.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class DraggableOrderedListComponent implements OnInit {
|
||||
export class DraggableOrderedListComponent {
|
||||
|
||||
@Input() accessibilityMode: boolean = false;
|
||||
/**
|
||||
@ -32,9 +32,6 @@ export class DraggableOrderedListComponent implements OnInit {
|
||||
|
||||
constructor(private readonly cdRef: ChangeDetectorRef) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
drop(event: CdkDragDrop<string[]>) {
|
||||
if (event.previousIndex === event.currentIndex) return;
|
||||
moveItemInArray(this.items, event.previousIndex, event.currentIndex);
|
||||
|
@ -29,7 +29,7 @@
|
||||
</h5>
|
||||
<div class="ps-1 d-none d-md-inline-block">
|
||||
<ng-container *ngIf="item.seriesFormat | mangaFormat as formatString">
|
||||
<i class="fa {{item.seriesFormat | mangaFormatIcon}}" aria-hidden="true" *ngIf="item.seriesFormat != MangaFormat.UNKNOWN" title="{{formatString}}"></i>
|
||||
<i class="fa {{item.seriesFormat | mangaFormatIcon}}" aria-hidden="true" *ngIf="item.seriesFormat !== MangaFormat.UNKNOWN" title="{{formatString}}"></i>
|
||||
<span class="visually-hidden">{{formatString}}</span>
|
||||
</ng-container>
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { UtilityService } from 'src/app/shared/_services/utility.service';
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { LibraryType } from 'src/app/_models/library';
|
||||
import { MangaFormat } from 'src/app/_models/manga-format';
|
||||
import { ReadingListItem } from 'src/app/_models/reading-list';
|
||||
@ -11,7 +10,7 @@ import { ImageService } from 'src/app/_services/image.service';
|
||||
styleUrls: ['./reading-list-item.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ReadingListItemComponent implements OnInit {
|
||||
export class ReadingListItemComponent {
|
||||
|
||||
@Input() item!: ReadingListItem;
|
||||
@Input() position: number = 0;
|
||||
@ -28,10 +27,7 @@ export class ReadingListItemComponent implements OnInit {
|
||||
return MangaFormat;
|
||||
}
|
||||
|
||||
constructor(public imageService: ImageService, private utilityService: UtilityService,
|
||||
private readonly cdRef: ChangeDetectorRef) { }
|
||||
|
||||
ngOnInit(): void {}
|
||||
constructor(public imageService: ImageService) { }
|
||||
|
||||
readChapter(item: ReadingListItem) {
|
||||
this.read.emit(item);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { FormGroup, FormControl, Validators } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
@ -15,7 +15,7 @@ import { MemberService } from 'src/app/_services/member.service';
|
||||
styleUrls: ['./register.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class RegisterComponent implements OnInit {
|
||||
export class RegisterComponent {
|
||||
|
||||
registerForm: FormGroup = new FormGroup({
|
||||
email: new FormControl('', [Validators.email]),
|
||||
@ -34,9 +34,6 @@ export class RegisterComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
submit() {
|
||||
const model = this.registerForm.getRawValue();
|
||||
this.accountService.register(model).subscribe((user) => {
|
||||
|
@ -138,7 +138,7 @@
|
||||
<div class="card-container row g-0" #container>
|
||||
<ng-container *ngFor="let item of scroll.viewPortItems; let idx = index; trackBy: trackByStoryLineIdentity">
|
||||
<ng-container *ngIf="!item.isChapter; else chapterCardItem">
|
||||
<app-card-item class="col-auto mt-2 mb-2" *ngIf="item.volume.number != 0" [entity]="item.volume" [title]="item.volume.name" (click)="openVolume(item.volume)"
|
||||
<app-card-item class="col-auto mt-2 mb-2" *ngIf="item.volume.number !== 0" [entity]="item.volume" [title]="item.volume.name" (click)="openVolume(item.volume)"
|
||||
[imageUrl]="imageService.getVolumeCoverImage(item.volume.id)"
|
||||
[read]="item.volume.pagesRead" [total]="item.volume.pages" [actions]="volumeActions"
|
||||
(selection)="bulkSelectionService.handleCardSelection('volume', scroll.viewPortInfo.startIndexWithBuffer + idx, volumes.length, $event)"
|
||||
@ -159,7 +159,7 @@
|
||||
<ng-container *ngFor="let item of scroll.viewPortItems; let idx = index; trackBy: trackByStoryLineIdentity">
|
||||
<ng-container *ngIf="!item.isChapter; else chapterListItem">
|
||||
<app-list-item [imageUrl]="imageService.getVolumeCoverImage(item.volume.id)"
|
||||
[seriesName]="series.name" [entity]="item.volume" *ngIf="item.volume.number != 0"
|
||||
[seriesName]="series.name" [entity]="item.volume" *ngIf="item.volume.number !== 0"
|
||||
[actions]="volumeActions" [libraryType]="libraryType" imageWidth="130px" imageHeight=""
|
||||
[pagesRead]="item.volume.pagesRead" [totalPages]="item.volume.pages" (read)="openVolume(item.volume)"
|
||||
[blur]="user?.preferences?.blurUnreadSummaries || false">
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { Directive, Input, HostListener, OnInit, ElementRef, Inject } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
// eslint-disable-next-line @angular-eslint/directive-selector
|
||||
selector: '[a11y-click]'
|
||||
})
|
||||
export class A11yClickDirective {
|
||||
export class A11yClickDirective implements OnInit {
|
||||
@Input('a11y-click') keyCodes!: string;
|
||||
keyCodeArray!: string[];
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-loading',
|
||||
@ -6,7 +6,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit }
|
||||
styleUrls: ['./loading.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class LoadingComponent implements OnInit {
|
||||
export class LoadingComponent {
|
||||
|
||||
@Input() loading: boolean = false;
|
||||
@Input() message: string = '';
|
||||
@ -15,10 +15,5 @@ export class LoadingComponent implements OnInit {
|
||||
*/
|
||||
@Input() absolute: boolean = false;
|
||||
|
||||
constructor(private readonly cdRef: ChangeDetectorRef) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
console.log('absolute: ', this.absolute);
|
||||
}
|
||||
|
||||
constructor() { }
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { Person } from '../../_models/metadata/person';
|
||||
|
||||
@Component({
|
||||
@ -7,13 +7,9 @@ import { Person } from '../../_models/metadata/person';
|
||||
styleUrls: ['./person-badge.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class PersonBadgeComponent implements OnInit {
|
||||
export class PersonBadgeComponent {
|
||||
|
||||
@Input() person!: Person;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
<ng-container *ngIf="format != MangaFormat.UNKNOWN">
|
||||
<ng-container *ngIf="format !== MangaFormat.UNKNOWN">
|
||||
<i class="fa {{format | mangaFormatIcon}}" aria-hidden="true" title="{{format | mangaFormat}}"></i>
|
||||
<ng-content></ng-content>
|
||||
</ng-container>
|
@ -15,7 +15,7 @@
|
||||
<div class="active-highlight"></div>
|
||||
<span class="phone-hidden" title="{{title}}">
|
||||
<div>
|
||||
<ng-container *ngIf="imageUrl != null && imageUrl != ''; else iconImg">
|
||||
<ng-container *ngIf="imageUrl !== null && imageUrl !== ''; else iconImg">
|
||||
<img [src]="imageUrl" alt="icon" class="side-nav-img">
|
||||
</ng-container>
|
||||
<ng-template #iconImg><i class="fa {{icon}}" aria-hidden="true"></i></ng-template>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<ng-container>
|
||||
<div class="side-nav" [ngClass]="{'closed' : (navService.sideNavCollapsed$ | async), 'hidden' :!(navService.sideNavVisibility$ | async)}" *ngIf="accountService.currentUser$ | async as user">
|
||||
<div class="side-nav" [ngClass]="{'closed' : (navService.sideNavCollapsed$ | async), 'hidden': (navService.sideNavVisibility$ | async) === false}" *ngIf="accountService.currentUser$ | async as user">
|
||||
<!-- <app-side-nav-item icon="fa-user-circle align-self-center phone-hidden" [title]="user.username | sentenceCase" link="/preferences/">
|
||||
<ng-container actions>
|
||||
Todo: This will be customize dashboard/side nav controls
|
||||
@ -13,7 +13,7 @@
|
||||
<app-side-nav-item icon="fa-list-ol" title="Reading Lists" link="/lists/"></app-side-nav-item>
|
||||
<app-side-nav-item icon="fa-bookmark" title="Bookmarks" link="/bookmarks/"></app-side-nav-item>
|
||||
<app-side-nav-item icon="fa-regular fa-rectangle-list" title="All Series" link="/all-series/" *ngIf="libraries.length > 0"></app-side-nav-item>
|
||||
<div class="mb-2 mt-3 ms-2 me-2" *ngIf="libraries.length > 10 && !(navService?.sideNavCollapsed$ | async)">
|
||||
<div class="mb-2 mt-3 ms-2 me-2" *ngIf="libraries.length > 10 && (navService?.sideNavCollapsed$ | async) === false">
|
||||
<label for="filter" class="form-label visually-hidden">Filter</label>
|
||||
<div class="form-group">
|
||||
<input id="filter" autocomplete="off" class="form-control" [(ngModel)]="filterQuery" type="text" aria-describedby="reset-input">
|
||||
|
@ -40,7 +40,7 @@
|
||||
</div>
|
||||
<div *ngIf="!isAddLibrary">
|
||||
Last Scanned:
|
||||
<span *ngIf="library.lastScanned == '0001-01-01T00:00:00'; else activeDate">Never</span>
|
||||
<span *ngIf="library.lastScanned === '0001-01-01T00:00:00'; else activeDate">Never</span>
|
||||
<ng-template #activeDate>
|
||||
{{library.lastScanned | date: 'short'}}
|
||||
</ng-template>
|
||||
@ -157,7 +157,7 @@
|
||||
<button type="button" class="btn btn-light" (click)="reset()">Reset</button>
|
||||
<button type="button" class="btn btn-secondary" (click)="close()">Cancel</button>
|
||||
|
||||
<ng-container *ngIf="isAddLibrary && setupStep != 3; else editLibraryButton">
|
||||
<ng-container *ngIf="isAddLibrary && setupStep !== 3; else editLibraryButton">
|
||||
<button type="button" class="btn btn-primary" (click)="nextStep()" [disabled]="isNextDisabled() || libraryForm.invalid">Next</button>
|
||||
</ng-container>
|
||||
<ng-template #editLibraryButton>
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { LegendPosition } from '@swimlane/ngx-charts';
|
||||
import { Subject, combineLatest, map, takeUntil, Observable } from 'rxjs';
|
||||
import { Subject, map, takeUntil, Observable } from 'rxjs';
|
||||
import { DayOfWeek, StatisticsService } from 'src/app/_services/statistics.service';
|
||||
import { compare } from 'src/app/_single-module/table/_directives/sortable-header.directive';
|
||||
import { PieDataItem } from '../../_models/pie-data-item';
|
||||
import { StatCount } from '../../_models/stat-count';
|
||||
import { DayOfWeekPipe } from '../../_pipes/day-of-week.pipe';
|
||||
@ -13,7 +12,7 @@ import { DayOfWeekPipe } from '../../_pipes/day-of-week.pipe';
|
||||
templateUrl: './day-breakdown.component.html',
|
||||
styleUrls: ['./day-breakdown.component.scss']
|
||||
})
|
||||
export class DayBreakdownComponent implements OnInit {
|
||||
export class DayBreakdownComponent implements OnDestroy {
|
||||
|
||||
private readonly onDestroy = new Subject<void>();
|
||||
|
||||
@ -42,11 +41,6 @@ export class DayBreakdownComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.onDestroy.next();
|
||||
this.onDestroy.complete();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.onDestroy.next();
|
||||
this.onDestroy.complete();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit, QueryList, ViewChildren } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, QueryList, ViewChildren } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { LegendPosition } from '@swimlane/ngx-charts';
|
||||
import { Observable, Subject, map, takeUntil, combineLatest, BehaviorSubject } from 'rxjs';
|
||||
@ -12,7 +12,7 @@ import { PieDataItem } from '../../_models/pie-data-item';
|
||||
styleUrls: ['./publication-status-stats.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class PublicationStatusStatsComponent implements OnInit {
|
||||
export class PublicationStatusStatsComponent implements OnDestroy {
|
||||
|
||||
@ViewChildren(SortableHeader<PieDataItem>) headers!: QueryList<SortableHeader<PieDataItem>>;
|
||||
|
||||
@ -48,11 +48,6 @@ export class PublicationStatusStatsComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.onDestroy.next();
|
||||
this.onDestroy.complete();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.onDestroy.next();
|
||||
this.onDestroy.complete();
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component, HostListener, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, HostListener, OnDestroy } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { BehaviorSubject, map, Observable, of, shareReplay, Subject, takeUntil, tap } from 'rxjs';
|
||||
import { BehaviorSubject, map, Observable, shareReplay, Subject, takeUntil } from 'rxjs';
|
||||
import { FilterQueryParam } from 'src/app/shared/_services/filter-utilities.service';
|
||||
import { Breakpoint, UtilityService } from 'src/app/shared/_services/utility.service';
|
||||
import { Series } from 'src/app/_models/series';
|
||||
@ -18,7 +18,7 @@ import { GenericListModalComponent } from '../_modals/generic-list-modal/generic
|
||||
styleUrls: ['./server-stats.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ServerStatsComponent implements OnInit, OnDestroy {
|
||||
export class ServerStatsComponent implements OnDestroy {
|
||||
|
||||
releaseYears$!: Observable<Array<PieDataItem>>;
|
||||
mostActiveUsers$!: Observable<Array<PieDataItem>>;
|
||||
@ -89,9 +89,6 @@ export class ServerStatsComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.onDestroy.next();
|
||||
this.onDestroy.complete();
|
||||
|
@ -5,7 +5,7 @@
|
||||
<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="tooltip" role="button" tabindex="0" *ngIf="description && description.length > 0"></i>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item" [ngClass]="{'underline': handleClick != undefined}" *ngFor="let item of data" (click)="doClick(item)">
|
||||
<li class="list-group-item" [ngClass]="{'underline': handleClick !== undefined}" *ngFor="let item of data" (click)="doClick(item)">
|
||||
<ng-container *ngIf="image && image(item) as url">
|
||||
<app-image *ngIf="url && url.length > 0" width="32px" maxHeight="32px" class="img-top me-1" [imageUrl]="url"></app-image>
|
||||
</ng-container>
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { CompactNumberPipe } from 'src/app/pipe/compact-number.pipe';
|
||||
import { FilterQueryParam } from 'src/app/shared/_services/filter-utilities.service';
|
||||
import { StatisticsService } from 'src/app/_services/statistics.service';
|
||||
import { GenericListModalComponent } from '../_modals/generic-list-modal/generic-list-modal.component';
|
||||
|
||||
@ -11,7 +10,7 @@ import { GenericListModalComponent } from '../_modals/generic-list-modal/generic
|
||||
styleUrls: ['./user-stats-info-cards.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class UserStatsInfoCardsComponent implements OnInit {
|
||||
export class UserStatsInfoCardsComponent {
|
||||
|
||||
@Input() totalPagesRead: number = 0;
|
||||
@Input() totalWordsRead: number = 0;
|
||||
@ -22,9 +21,6 @@ export class UserStatsInfoCardsComponent implements OnInit {
|
||||
|
||||
constructor(private statsService: StatisticsService, private modalService: NgbModal) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
openPageByYearList() {
|
||||
const numberPipe = new CompactNumberPipe();
|
||||
this.statsService.getPagesPerYear().subscribe(yearCounts => {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { Observable, of, Subject, takeUntil, shareReplay, map, take } from 'rxjs';
|
||||
import { AgeRestriction } from 'src/app/_models/metadata/age-restriction';
|
||||
@ -12,7 +12,7 @@ import { AccountService } from 'src/app/_services/account.service';
|
||||
styleUrls: ['./change-age-restriction.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ChangeAgeRestrictionComponent implements OnInit {
|
||||
export class ChangeAgeRestrictionComponent implements OnInit, OnDestroy {
|
||||
|
||||
user: User | undefined = undefined;
|
||||
hasChangeAgeRestrictionAbility: Observable<boolean> = of(false);
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
<div class="row g-0 mt-2">
|
||||
<h4>Devices</h4>
|
||||
<p *ngIf="devices.length == 0">
|
||||
<p *ngIf="devices.length === 0">
|
||||
There are no devices setup yet
|
||||
</p>
|
||||
<ng-container *ngFor="let device of devices">
|
||||
|
@ -12,7 +12,7 @@ import { AccountService } from 'src/app/_services/account.service';
|
||||
styleUrls: ['./theme-manager.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ThemeManagerComponent implements OnInit, OnDestroy {
|
||||
export class ThemeManagerComponent implements OnDestroy {
|
||||
|
||||
currentTheme: SiteTheme | undefined;
|
||||
isAdmin: boolean = false;
|
||||
@ -40,9 +40,6 @@ export class ThemeManagerComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.onDestroy.next();
|
||||
this.onDestroy.complete();
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { AfterContentChecked, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
import { Subject, take, debounceTime, takeUntil } from 'rxjs';
|
||||
@ -27,7 +27,7 @@ import { SeriesService } from 'src/app/_services/series.service';
|
||||
styleUrls: ['./want-to-read.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class WantToReadComponent implements OnInit, OnDestroy {
|
||||
export class WantToReadComponent implements OnInit, OnDestroy, AfterContentChecked {
|
||||
|
||||
@ViewChild('scrollingBlock') scrollingBlock: ElementRef<HTMLDivElement> | undefined;
|
||||
@ViewChild('companionBar') companionBar: ElementRef<HTMLDivElement> | undefined;
|
||||
|
@ -1,3 +1,5 @@
|
||||
/// <reference types="@angular/localize" />
|
||||
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { AppModule } from './app/app.module';
|
||||
|
@ -1,67 +0,0 @@
|
||||
/***************************************************************************************************
|
||||
* Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
|
||||
*/
|
||||
import '@angular/localize/init';
|
||||
/**
|
||||
* This file includes polyfills needed by Angular and is loaded before the app.
|
||||
* You can add your own extra polyfills to this file.
|
||||
*
|
||||
* This file is divided into 2 sections:
|
||||
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
|
||||
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
|
||||
* file.
|
||||
*
|
||||
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
|
||||
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
|
||||
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
|
||||
*
|
||||
* Learn more in https://angular.io/guide/browser-support
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
* BROWSER POLYFILLS
|
||||
*/
|
||||
|
||||
/** IE11 requires the following for NgClass support on SVG elements */
|
||||
// import 'classlist.js'; // Run `npm install --save classlist.js`.
|
||||
|
||||
/**
|
||||
* Web Animations `@angular/platform-browser/animations`
|
||||
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
|
||||
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
|
||||
*/
|
||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
|
||||
/**
|
||||
* By default, zone.js will patch all possible macroTask and DomEvents
|
||||
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
||||
* because those flags need to be set before `zone.js` being loaded, and webpack
|
||||
* will put import in the top of bundle, so user need to create a separate file
|
||||
* in this directory (for example: zone-flags.ts), and put the following flags
|
||||
* into that file, and then add the following code before importing zone.js.
|
||||
* import './zone-flags';
|
||||
*
|
||||
* The flags allowed in zone-flags.ts are listed here.
|
||||
*
|
||||
* The following flags will work for all browsers.
|
||||
*
|
||||
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
||||
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
||||
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
||||
*
|
||||
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
||||
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
||||
*
|
||||
* (window as any).__Zone_enable_cross_context_check = true;
|
||||
*
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
* Zone JS is required by default for Angular itself.
|
||||
*/
|
||||
import 'zone.js'; // Included with Angular CLI.
|
||||
|
||||
|
||||
/***************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
*/
|
@ -1,4 +1,4 @@
|
||||
@use '../node_modules/swiper/swiper.scss' as swiper;
|
||||
@import '../node_modules/swiper/swiper';
|
||||
|
||||
|
||||
// Import themes which define the css variables we use to customize the app
|
||||
@ -9,7 +9,8 @@
|
||||
@import './theme/variables';
|
||||
|
||||
// Bootstrap must be after _colors since we define the colors there
|
||||
@import '~bootstrap/scss/bootstrap';
|
||||
@import '../node_modules/bootstrap/scss/bootstrap';
|
||||
|
||||
|
||||
|
||||
// Import all the customized theme overrides
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* Default styles for Kavita */
|
||||
:root {
|
||||
//@import './dark.scss'; // Just re-import variables from dark since that's all we support
|
||||
--color-scheme: dark;
|
||||
--primary-color: #4ac694;
|
||||
--primary-color-dark-shade: #3B9E76;
|
||||
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"cookies": [],
|
||||
"origins": []
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"cookies": [],
|
||||
"origins": []
|
||||
}
|
@ -3,11 +3,12 @@
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
"types": [
|
||||
"@angular/localize"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts",
|
||||
"src/polyfills.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.d.ts"
|
||||
|
@ -4,7 +4,7 @@
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "./dist/out-tsc",
|
||||
"removeComments": true /* Do not emit comments to output. */,
|
||||
"removeComments": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitReturns": true,
|
||||
@ -16,15 +16,16 @@
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"target": "ES6",
|
||||
"target": "ES2022",
|
||||
"module": "es2020",
|
||||
"useDefineForClassFields": false,
|
||||
"lib": [
|
||||
"es2019",
|
||||
"es2018",
|
||||
"ES2022",
|
||||
"dom"
|
||||
]
|
||||
],
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true,
|
||||
|
@ -1,155 +0,0 @@
|
||||
{
|
||||
"extends": "tslint:recommended",
|
||||
"rulesDirectory": [
|
||||
"codelyzer"
|
||||
],
|
||||
"rules": {
|
||||
"align": {
|
||||
"options": [
|
||||
"parameters",
|
||||
"statements"
|
||||
]
|
||||
},
|
||||
"array-type": false,
|
||||
"arrow-return-shorthand": true,
|
||||
"curly": true,
|
||||
"deprecation": {
|
||||
"severity": "warning"
|
||||
},
|
||||
"eofline": true,
|
||||
"import-blacklist": [
|
||||
true,
|
||||
"rxjs/Rx"
|
||||
],
|
||||
"import-spacing": true,
|
||||
"indent": {
|
||||
"options": [
|
||||
"spaces"
|
||||
]
|
||||
},
|
||||
"max-classes-per-file": false,
|
||||
"max-line-length": [
|
||||
false,
|
||||
140
|
||||
],
|
||||
"member-ordering": [
|
||||
true,
|
||||
{
|
||||
"order": [
|
||||
"static-field",
|
||||
"instance-field",
|
||||
"static-method",
|
||||
"instance-method"
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-console": [
|
||||
true,
|
||||
"debug",
|
||||
"info",
|
||||
"time",
|
||||
"timeEnd",
|
||||
"trace"
|
||||
],
|
||||
"no-empty": false,
|
||||
"no-inferrable-types": [
|
||||
false,
|
||||
"ignore-params"
|
||||
],
|
||||
"no-non-null-assertion": true,
|
||||
"no-redundant-jsdoc": true,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-var-requires": false,
|
||||
"object-literal-key-quotes": [
|
||||
true,
|
||||
"as-needed"
|
||||
],
|
||||
"quotemark": [
|
||||
true,
|
||||
"single"
|
||||
],
|
||||
"semicolon": {
|
||||
"options": [
|
||||
"always"
|
||||
]
|
||||
},
|
||||
"space-before-function-paren": {
|
||||
"options": {
|
||||
"anonymous": "never",
|
||||
"asyncArrow": "always",
|
||||
"constructor": "never",
|
||||
"method": "never",
|
||||
"named": "never"
|
||||
}
|
||||
},
|
||||
"typedef": [
|
||||
false,
|
||||
"call-signature"
|
||||
],
|
||||
"typedef-whitespace": {
|
||||
"options": [
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
},
|
||||
{
|
||||
"call-signature": "onespace",
|
||||
"index-signature": "onespace",
|
||||
"parameter": "onespace",
|
||||
"property-declaration": "onespace",
|
||||
"variable-declaration": "onespace"
|
||||
}
|
||||
]
|
||||
},
|
||||
"variable-name": {
|
||||
"options": [
|
||||
"ban-keywords",
|
||||
"check-format",
|
||||
"allow-pascal-case"
|
||||
]
|
||||
},
|
||||
"whitespace": {
|
||||
"options": [
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-separator",
|
||||
"check-type",
|
||||
"check-typecast"
|
||||
]
|
||||
},
|
||||
"component-class-suffix": true,
|
||||
"contextual-lifecycle": true,
|
||||
"directive-class-suffix": true,
|
||||
"no-conflicting-lifecycle": true,
|
||||
"no-host-metadata-property": true,
|
||||
"no-input-rename": true,
|
||||
"no-inputs-metadata-property": true,
|
||||
"no-output-native": true,
|
||||
"no-output-on-prefix": true,
|
||||
"no-output-rename": true,
|
||||
"no-outputs-metadata-property": true,
|
||||
"template-banana-in-box": true,
|
||||
"template-no-negated-async": true,
|
||||
"use-lifecycle-interface": true,
|
||||
"use-pipe-transform-interface": true,
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
"app",
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
"app",
|
||||
"kebab-case"
|
||||
],
|
||||
"comment-format": [
|
||||
false
|
||||
]
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
"name": "GPL-3.0",
|
||||
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
||||
},
|
||||
"version": "0.6.1.29"
|
||||
"version": "0.6.1.30"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user