mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-04-24 15:19:29 +00:00
feat: separate prettier from eslint
This commit is contained in:
committed by
Thea Schöbl
parent
939fb6ef0f
commit
a88d000ccd
@@ -1,27 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {chunk} from './chunk';
|
||||
|
||||
describe('chunk', function () {
|
||||
it('should chunk items in the correct sizes', function () {
|
||||
expect(chunk([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3)).toEqual([
|
||||
[1, 2, 3],
|
||||
[4, 5, 6],
|
||||
[7, 8, 9],
|
||||
[10],
|
||||
]);
|
||||
expect(chunk([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3)).toEqual([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,26 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the difference between two arrays.
|
||||
*/
|
||||
export function differenceBy<T>(
|
||||
a: T[],
|
||||
b: T[],
|
||||
transform: (item: T) => unknown,
|
||||
) {
|
||||
export function differenceBy<T>(a: T[], b: T[], transform: (item: T) => unknown) {
|
||||
const disallowed = new Set(b.map(transform));
|
||||
|
||||
return a.filter(item => !disallowed.has(transform(item)));
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Group an array by a function
|
||||
*/
|
||||
export function groupBy<T>(
|
||||
collection: T[],
|
||||
group: (item: T) => string | undefined,
|
||||
): Record<string, T[]> {
|
||||
export function groupBy<T>(collection: T[], group: (item: T) => string | undefined): Record<string, T[]> {
|
||||
return collection.reduce((accumulator: Record<string, T[]>, item) => {
|
||||
const key = group(item) ?? '';
|
||||
accumulator[key] = accumulator[key] ?? [];
|
||||
@@ -31,10 +28,7 @@ export function groupBy<T>(
|
||||
/**
|
||||
* Group an array by a function (returns a Map, whose keys keep order info of items entry)
|
||||
*/
|
||||
export function groupByStable<T>(
|
||||
collection: T[],
|
||||
group: (item: T) => string | undefined,
|
||||
): Map<string, T[]> {
|
||||
export function groupByStable<T>(collection: T[], group: (item: T) => string | undefined): Map<string, T[]> {
|
||||
return collection.reduce((accumulator: Map<string, T[]>, item) => {
|
||||
const key = group(item) ?? '';
|
||||
accumulator.set(key, accumulator.get(key) ?? []);
|
||||
@@ -46,9 +40,6 @@ export function groupByStable<T>(
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export function groupByProperty<T extends object>(
|
||||
collection: T[],
|
||||
property: keyof T,
|
||||
): Record<string, T[]> {
|
||||
export function groupByProperty<T extends object>(collection: T[], property: keyof T): Record<string, T[]> {
|
||||
return groupBy(collection, item => item[property] as unknown as string);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -19,10 +19,7 @@
|
||||
* each key is the last element responsible for generating the key. The
|
||||
* iteratee is invoked with one argument: (value).
|
||||
*/
|
||||
export function keyBy<T>(
|
||||
collection: T[],
|
||||
key: (item: T) => string | number,
|
||||
): Record<string, T> {
|
||||
export function keyBy<T>(collection: T[], key: (item: T) => string | number): Record<string, T> {
|
||||
return collection.reduce((accumulator, item) => {
|
||||
accumulator[key(item)] = item;
|
||||
return accumulator;
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the minimum value of a collection.
|
||||
*/
|
||||
export function minBy<T>(
|
||||
array: T[],
|
||||
transform: (item: T) => number | undefined,
|
||||
): T {
|
||||
export function minBy<T>(array: T[], transform: (item: T) => number | undefined): T {
|
||||
const transforms = array.map(transform);
|
||||
const min = Math.min(...(transforms.filter(it => !!it) as number[]));
|
||||
return array.find((_, i) => transforms[i] === min) as T;
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns a new object without the specified keys.
|
||||
*/
|
||||
export function omit<T extends object, U extends keyof T>(
|
||||
object: T,
|
||||
...keys: U[]
|
||||
): Omit<T, U> {
|
||||
export function omit<T extends object, U extends keyof T>(object: T, ...keys: U[]): Omit<T, U> {
|
||||
const out = {...object};
|
||||
for (const key of keys) delete out[key];
|
||||
return out as Exclude<T, U>;
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Partitions a list into two lists. One with the elements that satisfy a predicate,
|
||||
* and one with the elements that don't satisfy the predicate.
|
||||
*/
|
||||
export function partition<T>(
|
||||
array: T[],
|
||||
transform: (item: T) => boolean,
|
||||
): [T[], T[]] {
|
||||
export function partition<T>(array: T[], transform: (item: T) => boolean): [T[], T[]] {
|
||||
return array.reduce<[T[], T[]]>(
|
||||
(accumulator, item) => {
|
||||
accumulator[transform(item) ? 0 : 1].push(item);
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Pick a set of properties from an object
|
||||
*/
|
||||
export function pick<T extends object, U extends keyof T>(
|
||||
object: T,
|
||||
keys: U[],
|
||||
): Pick<T, U> {
|
||||
export function pick<T extends object, U extends keyof T>(object: T, keys: U[]): Pick<T, U> {
|
||||
return keys.reduce((accumulator, key) => {
|
||||
if (object.hasOwnProperty(key)) {
|
||||
accumulator[key] = object[key];
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {stringSort, stringSortBy} from './string-sort';
|
||||
@@ -23,10 +23,11 @@ describe('stringSort', () => {
|
||||
|
||||
describe('stringSortBy', () => {
|
||||
it('should sort an array of strings', () => {
|
||||
expect(
|
||||
[{item: 'a'}, {item: 'c'}, {item: 'b'}, {item: 'd'}].sort(
|
||||
stringSortBy(it => it.item),
|
||||
),
|
||||
).toEqual([{item: 'a'}, {item: 'b'}, {item: 'c'}, {item: 'd'}]);
|
||||
expect([{item: 'a'}, {item: 'c'}, {item: 'b'}, {item: 'd'}].sort(stringSortBy(it => it.item))).toEqual([
|
||||
{item: 'a'},
|
||||
{item: 'b'},
|
||||
{item: 'c'},
|
||||
{item: 'd'},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -25,9 +25,7 @@ export function stringSort(a = '', b = ''): number {
|
||||
/**
|
||||
* sort function for two strings that allows for a custom transform
|
||||
*/
|
||||
export function stringSortBy<T>(
|
||||
map: (item: T) => string | undefined,
|
||||
): (a: T, b: T) => number {
|
||||
export function stringSortBy<T>(map: (item: T) => string | undefined): (a: T, b: T) => number {
|
||||
return (a: T, b: T): number => {
|
||||
const aValue = map(a) || '';
|
||||
const bValue = map(b) || '';
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -20,18 +20,12 @@ export function sumBy<T extends object>(
|
||||
collection: T[],
|
||||
transform: (value: T) => number | undefined,
|
||||
): number {
|
||||
return collection.reduce(
|
||||
(accumulator, item) => accumulator + (transform(item) || 0),
|
||||
0,
|
||||
);
|
||||
return collection.reduce((accumulator, item) => accumulator + (transform(item) || 0), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sum an array of numbers
|
||||
*/
|
||||
export function sum(collection: Array<number | undefined>): number {
|
||||
return collection.reduce<number>(
|
||||
(accumulator, item) => accumulator + (item || 0),
|
||||
0,
|
||||
);
|
||||
return collection.reduce<number>((accumulator, item) => accumulator + (item || 0), 0);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export type Tree<T> = {
|
||||
@@ -22,10 +22,7 @@ export type Tree<T> = {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export function treeGroupBy<T>(
|
||||
items: T[],
|
||||
transform: (item: T) => string[],
|
||||
): Tree<T> {
|
||||
export function treeGroupBy<T>(items: T[], transform: (item: T) => string[]): Tree<T> {
|
||||
const tree: Tree<T> = {};
|
||||
|
||||
for (const item of items) {
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {uniqBy} from './uniq';
|
||||
|
||||
describe('uniq', function () {
|
||||
it('should return an array with unique values', function () {
|
||||
const array = [
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
||||
];
|
||||
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
const result = uniqBy(array, it => it);
|
||||
expect(result).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
});
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Filter out duplicates from an array.
|
||||
*/
|
||||
export function uniqBy<T>(
|
||||
array: T[],
|
||||
transform: (item: T) => string | number,
|
||||
): T[] {
|
||||
export function uniqBy<T>(array: T[], transform: (item: T) => string | number): T[] {
|
||||
return Object.values(
|
||||
array.reduce((accumulator, current) => {
|
||||
accumulator[transform(current)] = current;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2020 StApps
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -13,12 +13,7 @@
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {
|
||||
SCSearchBooleanFilter,
|
||||
SCSearchFilter,
|
||||
SCSearchValueFilter,
|
||||
SCThing,
|
||||
} from '@openstapps/core';
|
||||
import {SCSearchBooleanFilter, SCSearchFilter, SCSearchValueFilter, SCThing} from '@openstapps/core';
|
||||
import {logger} from '../ts-logger';
|
||||
|
||||
/**
|
||||
@@ -36,9 +31,7 @@ export function checkFilter(thing: SCThing, filter: SCSearchFilter): boolean {
|
||||
return applyValueFilter(thing, filter);
|
||||
}
|
||||
|
||||
void logger.error(
|
||||
`Not implemented filter method "${filter.type}" in fake backend!`,
|
||||
);
|
||||
void logger.error(`Not implemented filter method "${filter.type}" in fake backend!`);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -46,16 +39,9 @@ export function checkFilter(thing: SCThing, filter: SCSearchFilter): boolean {
|
||||
/**
|
||||
* Checks if a value filter applies to an SCThing
|
||||
*/
|
||||
function applyValueFilter(
|
||||
thing: SCThing,
|
||||
filter: SCSearchValueFilter,
|
||||
): boolean {
|
||||
function applyValueFilter(thing: SCThing, filter: SCSearchValueFilter): boolean {
|
||||
const path = filter.arguments.field.split('.');
|
||||
const thingFieldValue = traverseToFieldPath(
|
||||
thing,
|
||||
path,
|
||||
filter.arguments.value,
|
||||
);
|
||||
const thingFieldValue = traverseToFieldPath(thing, path, filter.arguments.value);
|
||||
|
||||
if (!thingFieldValue.found) {
|
||||
return false;
|
||||
@@ -140,10 +126,7 @@ function traverseToFieldPath(
|
||||
/**
|
||||
* ES treats arrays like normal fields
|
||||
*/
|
||||
function esStyleFieldHandler<T>(
|
||||
field: T | T[],
|
||||
handler: (value: T) => FieldSearchResult,
|
||||
): FieldSearchResult {
|
||||
function esStyleFieldHandler<T>(field: T | T[], handler: (value: T) => FieldSearchResult): FieldSearchResult {
|
||||
if (Array.isArray(field)) {
|
||||
for (const nestedField of field) {
|
||||
const result = handler(nestedField);
|
||||
@@ -163,10 +146,7 @@ function esStyleFieldHandler<T>(
|
||||
/**
|
||||
* Checks if a boolean filter applies to an SCThing
|
||||
*/
|
||||
function applyBooleanFilter(
|
||||
thing: SCThing,
|
||||
filter: SCSearchBooleanFilter,
|
||||
): boolean {
|
||||
function applyBooleanFilter(thing: SCThing, filter: SCSearchBooleanFilter): boolean {
|
||||
let out = false;
|
||||
|
||||
switch (filter.arguments.operation) {
|
||||
@@ -192,9 +172,7 @@ function applyBooleanFilter(
|
||||
return false;
|
||||
}
|
||||
|
||||
void logger.error(
|
||||
`Not implemented boolean filter "${filter.arguments.operation}"`,
|
||||
);
|
||||
void logger.error(`Not implemented boolean filter "${filter.arguments.operation}"`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* Copyright (C) 2023 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -16,11 +16,13 @@ import {
|
||||
SCAboutPageContentType,
|
||||
SCAuthorizationProvider,
|
||||
SCBackendAggregationConfiguration,
|
||||
SCIndexResponse, SCSettingInputType, SCThingOriginType,
|
||||
SCThingType
|
||||
SCIndexResponse,
|
||||
SCSettingInputType,
|
||||
SCThingOriginType,
|
||||
SCThingType,
|
||||
} from '@openstapps/core';
|
||||
import {Polygon} from 'geojson';
|
||||
import packageJson from "../../../../package.json";
|
||||
import packageJson from '../../../../package.json';
|
||||
|
||||
// provides sample aggregations to be used in tests or backendless development
|
||||
export const sampleAggregations: SCBackendAggregationConfiguration[] = [
|
||||
@@ -62,12 +64,7 @@ export const sampleAggregations: SCBackendAggregationConfiguration[] = [
|
||||
},
|
||||
{
|
||||
fieldName: 'keywords',
|
||||
onlyOnTypes: [
|
||||
SCThingType.Article,
|
||||
SCThingType.Book,
|
||||
SCThingType.Message,
|
||||
SCThingType.Video,
|
||||
],
|
||||
onlyOnTypes: [SCThingType.Article, SCThingType.Book, SCThingType.Message, SCThingType.Video],
|
||||
},
|
||||
{
|
||||
fieldName: 'type',
|
||||
@@ -75,56 +72,41 @@ export const sampleAggregations: SCBackendAggregationConfiguration[] = [
|
||||
];
|
||||
|
||||
export const sampleAuthConfiguration: {
|
||||
default: SCAuthorizationProvider;
|
||||
paia: SCAuthorizationProvider;
|
||||
} = {
|
||||
default: {
|
||||
client: {clientId: '', scopes: '', url: ''},
|
||||
endpoints: {
|
||||
authorization: '',
|
||||
mapping: {id: '', name: ''},
|
||||
token: '',
|
||||
userinfo: '',
|
||||
},
|
||||
default: SCAuthorizationProvider;
|
||||
paia: SCAuthorizationProvider;
|
||||
} = {
|
||||
default: {
|
||||
client: {clientId: '', scopes: '', url: ''},
|
||||
endpoints: {
|
||||
authorization: '',
|
||||
mapping: {id: '', name: ''},
|
||||
token: '',
|
||||
userinfo: '',
|
||||
},
|
||||
paia: {
|
||||
client: {clientId: '', scopes: '', url: ''},
|
||||
endpoints: {
|
||||
authorization: '',
|
||||
mapping: {id: '', name: ''},
|
||||
token: '',
|
||||
userinfo: '',
|
||||
},
|
||||
},
|
||||
paia: {
|
||||
client: {clientId: '', scopes: '', url: ''},
|
||||
endpoints: {
|
||||
authorization: '',
|
||||
mapping: {id: '', name: ''},
|
||||
token: '',
|
||||
userinfo: '',
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const sampleDefaultPolygon: Polygon = {
|
||||
"coordinates": [
|
||||
coordinates: [
|
||||
[
|
||||
[
|
||||
8.660432999690723,
|
||||
50.123027017044436
|
||||
],
|
||||
[
|
||||
8.675496285518358,
|
||||
50.123027017044436
|
||||
],
|
||||
[
|
||||
8.675496285518358,
|
||||
50.13066176448642
|
||||
],
|
||||
[
|
||||
8.660432999690723,
|
||||
50.13066176448642
|
||||
],
|
||||
[
|
||||
8.660432999690723,
|
||||
50.123027017044436
|
||||
]
|
||||
]
|
||||
[8.660432999690723, 50.123027017044436],
|
||||
[8.675496285518358, 50.123027017044436],
|
||||
[8.675496285518358, 50.13066176448642],
|
||||
[8.660432999690723, 50.13066176448642],
|
||||
[8.660432999690723, 50.123027017044436],
|
||||
],
|
||||
],
|
||||
"type": "Polygon"
|
||||
}
|
||||
type: 'Polygon',
|
||||
};
|
||||
|
||||
const scVersion = packageJson.dependencies['@openstapps/core'];
|
||||
|
||||
@@ -246,20 +228,12 @@ export const sampleIndexResponse: SCIndexResponse = {
|
||||
},
|
||||
{
|
||||
fieldName: 'geo',
|
||||
onlyOnTypes: [
|
||||
SCThingType.Building,
|
||||
SCThingType.PointOfInterest,
|
||||
SCThingType.Room,
|
||||
],
|
||||
onlyOnTypes: [SCThingType.Building, SCThingType.PointOfInterest, SCThingType.Room],
|
||||
sortTypes: ['distance'],
|
||||
},
|
||||
{
|
||||
fieldName: 'geo',
|
||||
onlyOnTypes: [
|
||||
SCThingType.Building,
|
||||
SCThingType.PointOfInterest,
|
||||
SCThingType.Room,
|
||||
],
|
||||
onlyOnTypes: [SCThingType.Building, SCThingType.PointOfInterest, SCThingType.Room],
|
||||
sortTypes: ['distance'],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -359,30 +359,30 @@ const sampleDateSeries: SCDateSeries[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export const sampleThingsMap: { [key in SCThingType | string]: SCThing[] } = {
|
||||
export const sampleThingsMap: {[key in SCThingType | string]: SCThing[]} = {
|
||||
'academic event': sampleAcademicEvents,
|
||||
article: sampleArticles,
|
||||
book: sampleBooks,
|
||||
building: sampleBuildings,
|
||||
catalog: sampleCatalogs,
|
||||
'article': sampleArticles,
|
||||
'book': sampleBooks,
|
||||
'building': sampleBuildings,
|
||||
'catalog': sampleCatalogs,
|
||||
'course of studies': [],
|
||||
'date series': sampleDateSeries,
|
||||
diff: [],
|
||||
dish: sampleDishes,
|
||||
favorite: sampleFavorites,
|
||||
floor: [],
|
||||
message: sampleMessages,
|
||||
organization: [],
|
||||
person: samplePersons,
|
||||
'diff': [],
|
||||
'dish': sampleDishes,
|
||||
'favorite': sampleFavorites,
|
||||
'floor': [],
|
||||
'message': sampleMessages,
|
||||
'organization': [],
|
||||
'person': samplePersons,
|
||||
'point of interest': [],
|
||||
room: sampleRooms,
|
||||
semester: [],
|
||||
setting: [],
|
||||
'room': sampleRooms,
|
||||
'semester': [],
|
||||
'setting': [],
|
||||
'sport course': [],
|
||||
ticket: [],
|
||||
todo: sampleTodos,
|
||||
tour: [],
|
||||
video: [],
|
||||
'ticket': [],
|
||||
'todo': sampleTodos,
|
||||
'tour': [],
|
||||
'video': [],
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -407,7 +407,7 @@ export class SampleThings {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const sampleThings: any[] = [];
|
||||
for (const resource of sampleResources) {
|
||||
if (resource.instance.uid as SCThingType === uid) {
|
||||
if ((resource.instance.uid as SCThingType) === uid) {
|
||||
sampleThings.push(resource.instance);
|
||||
|
||||
return of(sampleThings);
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
import {
|
||||
HttpRequest,
|
||||
HttpHandler,
|
||||
HttpEvent,
|
||||
HttpInterceptor,
|
||||
HttpErrorResponse,
|
||||
} from '@angular/common/http';
|
||||
import {HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse} from '@angular/common/http';
|
||||
import {Observable, throwError} from 'rxjs';
|
||||
import {NGXLogger} from 'ngx-logger';
|
||||
import {catchError} from 'rxjs/operators';
|
||||
@@ -14,10 +23,7 @@ import {catchError} from 'rxjs/operators';
|
||||
export class ServiceHandlerInterceptor implements HttpInterceptor {
|
||||
constructor(private readonly logger: NGXLogger) {}
|
||||
|
||||
intercept(
|
||||
request: HttpRequest<unknown>,
|
||||
next: HttpHandler,
|
||||
): Observable<HttpEvent<unknown>> {
|
||||
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||
return next.handle(request).pipe(
|
||||
// Fixes the issue of errors dropping into "toPromise()"
|
||||
// and being not able to catch it in the "caller methods"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 StApps
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -76,10 +76,7 @@ export class SharedAxisChoreographer<T> {
|
||||
|
||||
// pre-place animation state
|
||||
// new element comes in from the right and pushes the old one to the left
|
||||
this.animationState =
|
||||
SHARED_AXIS_DIRECTIONS[
|
||||
direction ?? this.getDirection(this.currentValue, newValue)
|
||||
];
|
||||
this.animationState = SHARED_AXIS_DIRECTIONS[direction ?? this.getDirection(this.currentValue, newValue)];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 StApps
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -12,14 +12,7 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {
|
||||
animate,
|
||||
sequence,
|
||||
state,
|
||||
style,
|
||||
transition,
|
||||
trigger,
|
||||
} from '@angular/animations';
|
||||
import {animate, sequence, state, style, transition, trigger} from '@angular/animations';
|
||||
|
||||
/**
|
||||
* Fade transition
|
||||
@@ -51,10 +44,7 @@ export const materialManualFade = trigger('materialManualFade', [
|
||||
*/
|
||||
export const materialFadeThrough = trigger('materialFadeThrough', [
|
||||
state('in', style({transform: 'scale(100%)', opacity: 1})),
|
||||
transition(':enter', [
|
||||
style({transform: 'scale(80%)', opacity: 0}),
|
||||
animate('250ms ease'),
|
||||
]),
|
||||
transition(':enter', [style({transform: 'scale(80%)', opacity: 0}), animate('250ms ease')]),
|
||||
transition(':leave', [animate('200ms ease', style({opacity: 0}))]),
|
||||
]);
|
||||
|
||||
@@ -73,31 +63,16 @@ export const SHARED_AXIS_DIRECTIONS = {
|
||||
* @see {SharedAxisChoreographer}
|
||||
*/
|
||||
export const materialSharedAxisX = trigger('materialSharedAxisX', [
|
||||
state(
|
||||
SHARED_AXIS_DIRECTIONS[-1],
|
||||
style({opacity: 0, transform: 'translateX(30px)'}),
|
||||
),
|
||||
state(
|
||||
SHARED_AXIS_DIRECTIONS[0],
|
||||
style({opacity: 1, transform: 'translateX(0px)'}),
|
||||
),
|
||||
state(
|
||||
SHARED_AXIS_DIRECTIONS[1],
|
||||
style({opacity: 0, transform: 'translateX(-30px)'}),
|
||||
),
|
||||
state(SHARED_AXIS_DIRECTIONS[-1], style({opacity: 0, transform: 'translateX(30px)'})),
|
||||
state(SHARED_AXIS_DIRECTIONS[0], style({opacity: 1, transform: 'translateX(0px)'})),
|
||||
state(SHARED_AXIS_DIRECTIONS[1], style({opacity: 0, transform: 'translateX(-30px)'})),
|
||||
transition(
|
||||
`${SHARED_AXIS_DIRECTIONS[-1]} => ${SHARED_AXIS_DIRECTIONS[0]}`,
|
||||
sequence([
|
||||
style({opacity: 0, transform: 'translateX(-30px)'}),
|
||||
animate('100ms ease-out'),
|
||||
]),
|
||||
sequence([style({opacity: 0, transform: 'translateX(-30px)'}), animate('100ms ease-out')]),
|
||||
),
|
||||
transition(`${SHARED_AXIS_DIRECTIONS[0]} => *`, animate('100ms ease-out')),
|
||||
transition(
|
||||
`${SHARED_AXIS_DIRECTIONS[1]} => ${SHARED_AXIS_DIRECTIONS[0]}`,
|
||||
sequence([
|
||||
style({opacity: 0, transform: 'translateX(30px)'}),
|
||||
animate('100ms ease-out'),
|
||||
]),
|
||||
sequence([style({opacity: 0, transform: 'translateX(30px)'}), animate('100ms ease-out')]),
|
||||
),
|
||||
]);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/*
|
||||
* Copyright (C) 2018, 2019 StApps
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -13,6 +12,8 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
@@ -50,13 +51,8 @@ describe('AppComponent', () => {
|
||||
ready: platformReadySpy,
|
||||
is: platformIsSpy,
|
||||
});
|
||||
translateServiceSpy = jasmine.createSpyObj('TranslateService', [
|
||||
'setDefaultLang',
|
||||
'use',
|
||||
]);
|
||||
thingTranslateServiceSpy = jasmine.createSpyObj('ThingTranslateService', [
|
||||
'init',
|
||||
]);
|
||||
translateServiceSpy = jasmine.createSpyObj('TranslateService', ['setDefaultLang', 'use']);
|
||||
thingTranslateServiceSpy = jasmine.createSpyObj('ThingTranslateService', ['init']);
|
||||
settingsProvider = jasmine.createSpyObj('SettingsProvider', [
|
||||
'getSettingValue',
|
||||
'provideSetting',
|
||||
@@ -66,27 +62,15 @@ describe('AppComponent', () => {
|
||||
'getDifferences',
|
||||
'postDifferencesNotification',
|
||||
]);
|
||||
configProvider = jasmine.createSpyObj('ConfigProvider', [
|
||||
'init',
|
||||
'getAnyValue',
|
||||
]);
|
||||
configProvider = jasmine.createSpyObj('ConfigProvider', ['init', 'getAnyValue']);
|
||||
configProvider.getAnyValue = jasmine.createSpy().and.callFake(function () {
|
||||
return sampleAuthConfiguration;
|
||||
});
|
||||
ngxLogger = jasmine.createSpyObj('NGXLogger', ['log', 'error', 'warn']);
|
||||
storageProvider = jasmine.createSpyObj('StorageProvider', [
|
||||
'init',
|
||||
'get',
|
||||
'has',
|
||||
'put',
|
||||
]);
|
||||
storageProvider = jasmine.createSpyObj('StorageProvider', ['init', 'get', 'has', 'put']);
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterTestingModule.withRoutes([]),
|
||||
HttpClientTestingModule,
|
||||
AuthModule,
|
||||
],
|
||||
imports: [RouterTestingModule.withRoutes([]), HttpClientTestingModule, AuthModule],
|
||||
declarations: [AppComponent],
|
||||
providers: [
|
||||
{provide: Platform, useValue: platformSpy},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* Copyright (C) 2023 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -97,9 +97,7 @@ export class AppComponent implements AfterContentInit {
|
||||
await StatusBar.setStyle({style: Style.Dark});
|
||||
if (Capacitor.getPlatform() === 'android') {
|
||||
await StatusBar.setBackgroundColor({
|
||||
color: getComputedStyle(document.documentElement)
|
||||
.getPropertyValue('--ion-color-primary')
|
||||
.trim(),
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue('--ion-color-primary').trim(),
|
||||
});
|
||||
await StatusBar.setOverlaysWebView({overlay: false});
|
||||
await NavigationBar.setColor({
|
||||
@@ -113,12 +111,7 @@ export class AppComponent implements AfterContentInit {
|
||||
await this.authNotificationsInit();
|
||||
|
||||
// set order of categories in settings
|
||||
this.settingsProvider.setCategoriesOrder([
|
||||
'profile',
|
||||
'privacy',
|
||||
'credentials',
|
||||
'others',
|
||||
]);
|
||||
this.settingsProvider.setCategoriesOrder(['profile', 'privacy', 'credentials', 'others']);
|
||||
});
|
||||
|
||||
window.addEventListener('touchmove', this.touchMoveEvent, true);
|
||||
@@ -130,14 +123,10 @@ export class AppComponent implements AfterContentInit {
|
||||
private async authNotificationsInit() {
|
||||
this.authHelper
|
||||
.getProvider('default')
|
||||
.events$.subscribe(action =>
|
||||
this.showMessage(this.authHelper.getAuthMessage('default', action)),
|
||||
);
|
||||
.events$.subscribe(action => this.showMessage(this.authHelper.getAuthMessage('default', action)));
|
||||
this.authHelper
|
||||
.getProvider('paia')
|
||||
.events$.subscribe(action =>
|
||||
this.showMessage(this.authHelper.getAuthMessage('paia', action)),
|
||||
);
|
||||
.events$.subscribe(action => this.showMessage(this.authHelper.getAuthMessage('paia', action)));
|
||||
}
|
||||
|
||||
private async showMessage(message?: string) {
|
||||
@@ -158,9 +147,7 @@ export class AppComponent implements AfterContentInit {
|
||||
touchMoveEvent = (event: Event): void => {
|
||||
if (
|
||||
this.ommitedEventSources.includes(
|
||||
(event?.target as unknown as Record<string, string>)?.[
|
||||
's-hn'
|
||||
]?.toLowerCase(),
|
||||
(event?.target as unknown as Record<string, string>)?.['s-hn']?.toLowerCase(),
|
||||
)
|
||||
) {
|
||||
return;
|
||||
|
||||
@@ -12,27 +12,14 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {
|
||||
CommonModule,
|
||||
LocationStrategy,
|
||||
PathLocationStrategy,
|
||||
registerLocaleData,
|
||||
} from '@angular/common';
|
||||
import {
|
||||
HTTP_INTERCEPTORS,
|
||||
HttpClient,
|
||||
HttpClientModule,
|
||||
} from '@angular/common/http';
|
||||
import {CommonModule, LocationStrategy, PathLocationStrategy, registerLocaleData} from '@angular/common';
|
||||
import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule} from '@angular/common/http';
|
||||
import localeDe from '@angular/common/locales/de';
|
||||
import {APP_INITIALIZER, NgModule} from '@angular/core';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {RouteReuseStrategy} from '@angular/router';
|
||||
import {IonicModule, IonicRouteStrategy, Platform} from '@ionic/angular';
|
||||
import {
|
||||
TranslateLoader,
|
||||
TranslateModule,
|
||||
TranslateService,
|
||||
} from '@ngx-translate/core';
|
||||
import {TranslateLoader, TranslateModule, TranslateService} from '@ngx-translate/core';
|
||||
import {TranslateHttpLoader} from '@ngx-translate/http-loader';
|
||||
import moment from 'moment';
|
||||
import 'moment/min/locales';
|
||||
@@ -116,10 +103,7 @@ export function initializerFactory(
|
||||
translateService.getBrowserLang() as SCSettingValue,
|
||||
);
|
||||
}
|
||||
const languageCode = (await settingsProvider.getValue(
|
||||
'profile',
|
||||
'language',
|
||||
)) as string;
|
||||
const languageCode = (await settingsProvider.getValue('profile', 'language')) as string;
|
||||
// this language will be used as a fallback when a translation isn't found in the current language
|
||||
translateService.setDefaultLang('en');
|
||||
translateService.use(languageCode);
|
||||
@@ -187,9 +171,7 @@ export function createTranslateLoader(http: HttpClient) {
|
||||
UtilModule,
|
||||
// use maximal logging level when not in production, minimal (log only fatal errors) in production
|
||||
LoggerModule.forRoot({
|
||||
level: environment.production
|
||||
? NgxLoggerLevel.FATAL
|
||||
: NgxLoggerLevel.TRACE,
|
||||
level: environment.production ? NgxLoggerLevel.FATAL : NgxLoggerLevel.TRACE,
|
||||
}),
|
||||
],
|
||||
providers: [
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<ion-header>
|
||||
@@ -22,11 +22,7 @@
|
||||
<!-- TODO: translation -->
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<cdk-virtual-scroll-viewport
|
||||
itemSize="130"
|
||||
minBufferPx="1500"
|
||||
maxBufferPx="2000"
|
||||
>
|
||||
<cdk-virtual-scroll-viewport itemSize="130" minBufferPx="1500" maxBufferPx="2000">
|
||||
<ion-card
|
||||
*cdkVirtualFor="let license of licenses"
|
||||
[href]="license.url || license.repository"
|
||||
@@ -36,12 +32,7 @@
|
||||
<ion-card-header>
|
||||
<ion-card-title>
|
||||
{{ license.name }}
|
||||
<ion-icon
|
||||
size="16"
|
||||
weight="300"
|
||||
class="supertext-icon"
|
||||
name="open_in_browser"
|
||||
></ion-icon>
|
||||
<ion-icon size="16" weight="300" class="supertext-icon" name="open_in_browser"></ion-icon>
|
||||
</ion-card-title>
|
||||
|
||||
<ion-card-subtitle *ngIf="license.authors || license.publisher">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--
|
||||
~ Copyright (C) 2021 StApps
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
@@ -14,16 +14,11 @@
|
||||
-->
|
||||
|
||||
<div [ngSwitch]="content.type">
|
||||
<markdown
|
||||
[data]="'value' | translateSimple: content"
|
||||
*ngSwitchCase="'markdown'"
|
||||
></markdown>
|
||||
<markdown [data]="'value' | translateSimple: content" *ngSwitchCase="'markdown'"></markdown>
|
||||
<div *ngSwitchCase="'section'">
|
||||
<ion-card *ngIf="content.card; else noCard">
|
||||
<ion-card-header>
|
||||
<ion-card-title>{{
|
||||
'title' | translateSimple: content
|
||||
}}</ion-card-title>
|
||||
<ion-card-title>{{ 'title' | translateSimple: content }}</ion-card-title>
|
||||
</ion-card-header>
|
||||
<ion-card-content>
|
||||
<about-page-content [content]="content.content"></about-page-content>
|
||||
@@ -42,11 +37,7 @@
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
<ion-item *ngSwitchCase="'router link'" [routerLink]="content.link">
|
||||
<ion-icon
|
||||
*ngIf="content.icon"
|
||||
[name]="content.icon"
|
||||
slot="start"
|
||||
></ion-icon>
|
||||
<ion-icon *ngIf="content.icon" [name]="content.icon" slot="start"></ion-icon>
|
||||
<ion-label>{{ 'title' | translateSimple: content }}</ion-label>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
@@ -31,18 +31,11 @@ export class AboutPageComponent implements OnInit {
|
||||
|
||||
version = packageJson.version;
|
||||
|
||||
constructor(
|
||||
private readonly route: ActivatedRoute,
|
||||
private readonly configProvider: ConfigProvider,
|
||||
) {}
|
||||
constructor(private readonly route: ActivatedRoute, private readonly configProvider: ConfigProvider) {}
|
||||
|
||||
async ngOnInit() {
|
||||
const route = this.route.snapshot.url.map(it => it.path).join('/');
|
||||
this.content =
|
||||
(
|
||||
this.configProvider.getValue(
|
||||
'aboutPages',
|
||||
) as SCAppConfiguration['aboutPages']
|
||||
)[route] ?? {};
|
||||
(this.configProvider.getValue('aboutPages') as SCAppConfiguration['aboutPages'])[route] ?? {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~ Copyright (C) 2023 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<ion-header>
|
||||
@@ -18,20 +18,13 @@
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title *ngIf="content; else titleLoading">{{
|
||||
'title' | translateSimple: content
|
||||
}}</ion-title>
|
||||
<ion-title *ngIf="content; else titleLoading">{{ 'title' | translateSimple: content }}</ion-title>
|
||||
<ng-template #titleLoading>
|
||||
<ion-title
|
||||
><ion-skeleton-text animated="true"></ion-skeleton-text
|
||||
></ion-title>
|
||||
<ion-title><ion-skeleton-text animated="true"></ion-skeleton-text></ion-title>
|
||||
</ng-template>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content *ngIf="content">
|
||||
<ion-text color="primary">{{ appName }} v{{ version }}</ion-text>
|
||||
<about-page-content
|
||||
*ngFor="let element of content.content"
|
||||
[content]="element"
|
||||
></about-page-content>
|
||||
<about-page-content *ngFor="let element of content.content" [content]="element"></about-page-content>
|
||||
</ion-content>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
@@ -24,9 +24,7 @@ import {keyBy} from '../../_helpers/collections/key-by';
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export function toAssessmentMap(
|
||||
data: SCAssessment[],
|
||||
): Record<SCUuid, SCAssessment> {
|
||||
export function toAssessmentMap(data: SCAssessment[]): Record<SCUuid, SCAssessment> {
|
||||
return keyBy(
|
||||
uniqBy(
|
||||
[
|
||||
@@ -40,9 +38,7 @@ export function toAssessmentMap(
|
||||
...superAssessment,
|
||||
} as SCAssessment;
|
||||
superAssessmentCopy.origin = assessment.origin;
|
||||
superAssessmentCopy.superAssessments = array
|
||||
.slice(index + 1)
|
||||
.reverse();
|
||||
superAssessmentCopy.superAssessments = array.slice(index + 1).reverse();
|
||||
return superAssessmentCopy;
|
||||
}) ?? [],
|
||||
),
|
||||
@@ -76,37 +72,24 @@ export class AssessmentsProvider {
|
||||
readonly http: HttpClient,
|
||||
) {}
|
||||
|
||||
async getAssessment(
|
||||
uid: SCUuid,
|
||||
accessToken?: string | null,
|
||||
forceFetch = false,
|
||||
): Promise<SCAssessment> {
|
||||
async getAssessment(uid: SCUuid, accessToken?: string | null, forceFetch = false): Promise<SCAssessment> {
|
||||
await this.getAssessments(accessToken, forceFetch);
|
||||
|
||||
return (await this.assessments)[uid];
|
||||
}
|
||||
|
||||
async getAssessments(
|
||||
accessToken?: string | null,
|
||||
forceFetch = false,
|
||||
): Promise<SCAssessment[]> {
|
||||
async getAssessments(accessToken?: string | null, forceFetch = false): Promise<SCAssessment[]> {
|
||||
// again, this is a hack to get around the fact that the assessment service
|
||||
// is very aggressive how many requests you can make, so it can happen
|
||||
// during development that simply by reloading pages over and over again
|
||||
// the assessment service will block you
|
||||
if (accessToken === 'mock' && !this.cache) {
|
||||
this.cacheTimestamp = Date.now();
|
||||
this.cache = import('./assessment-mock-data.json').then(
|
||||
it => it.data as SCAssessment[],
|
||||
);
|
||||
this.cache = import('./assessment-mock-data.json').then(it => it.data as SCAssessment[]);
|
||||
this.assessments = this.cache.then(toAssessmentMap);
|
||||
}
|
||||
|
||||
if (
|
||||
this.cache &&
|
||||
!forceFetch &&
|
||||
Date.now() - this.cacheTimestamp < this.cacheMaxAge
|
||||
) {
|
||||
if (this.cache && !forceFetch && Date.now() - this.cacheTimestamp < this.cacheMaxAge) {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
@@ -116,9 +99,7 @@ export class AssessmentsProvider {
|
||||
this.cache = this.http
|
||||
.get<{data: SCAssessment[]}>(`${url}/${this.assessmentPath}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${
|
||||
accessToken ?? (await this.defaultAuth.getValidToken()).accessToken
|
||||
}`,
|
||||
Authorization: `Bearer ${accessToken ?? (await this.defaultAuth.getValidToken()).accessToken}`,
|
||||
},
|
||||
})
|
||||
.toPromise()
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {Component, Input, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {AssessmentsProvider} from '../assessments.provider';
|
||||
import {
|
||||
DataDetailComponent,
|
||||
ExternalDataLoadEvent,
|
||||
} from '../../data/detail/data-detail.component';
|
||||
import {DataDetailComponent, ExternalDataLoadEvent} from '../../data/detail/data-detail.component';
|
||||
import {NavController, ViewWillEnter} from '@ionic/angular';
|
||||
import {Subscription} from 'rxjs';
|
||||
import {DataRoutingService} from '../../data/data-routing.service';
|
||||
@@ -30,9 +27,7 @@ import {SCAssessment} from '@openstapps/core';
|
||||
templateUrl: 'assessments-detail.html',
|
||||
styleUrls: ['assessments-detail.scss'],
|
||||
})
|
||||
export class AssessmentsDetailComponent
|
||||
implements ViewWillEnter, OnInit, OnDestroy
|
||||
{
|
||||
export class AssessmentsDetailComponent implements ViewWillEnter, OnInit, OnDestroy {
|
||||
constructor(
|
||||
readonly route: ActivatedRoute,
|
||||
readonly assessmentsProvider: AssessmentsProvider,
|
||||
@@ -54,14 +49,11 @@ export class AssessmentsDetailComponent
|
||||
if (!this.dataPathAutoRouting) return;
|
||||
this.subscriptions.push(
|
||||
this.dataRoutingService.pathSelectListener().subscribe(item => {
|
||||
void this.navController.navigateBack(
|
||||
['assessments', 'detail', item.uid],
|
||||
{
|
||||
queryParams: {
|
||||
token: this.activatedRoute.snapshot.queryParamMap.get('token'),
|
||||
},
|
||||
void this.navController.navigateBack(['assessments', 'detail', item.uid], {
|
||||
queryParams: {
|
||||
token: this.activatedRoute.snapshot.queryParamMap.get('token'),
|
||||
},
|
||||
);
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -72,11 +64,7 @@ export class AssessmentsDetailComponent
|
||||
|
||||
getItem(event: ExternalDataLoadEvent) {
|
||||
this.assessmentsProvider
|
||||
.getAssessment(
|
||||
event.uid,
|
||||
this.route.snapshot.queryParamMap.get('token'),
|
||||
event.forceReload,
|
||||
)
|
||||
.getAssessment(event.uid, this.route.snapshot.queryParamMap.get('token'), event.forceReload)
|
||||
.then(assessment => {
|
||||
this.item = assessment;
|
||||
event.resolve(this.item);
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<stapps-data-list
|
||||
@@ -21,10 +21,7 @@
|
||||
(loadmore)="loadMore.emit($event)"
|
||||
>
|
||||
<ng-template let-item>
|
||||
<assessments-list-item
|
||||
[item]="item"
|
||||
[hideThumbnail]="singleType"
|
||||
></assessments-list-item>
|
||||
<assessments-list-item [item]="item" [hideThumbnail]="singleType"></assessments-list-item>
|
||||
</ng-template>
|
||||
<ng-container header>
|
||||
<ng-content select="[header]"></ng-content>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<stapps-simple-data-list
|
||||
@@ -20,9 +20,6 @@
|
||||
[autoRouting]="false"
|
||||
>
|
||||
<ng-template let-item>
|
||||
<assessments-list-item
|
||||
[item]="item"
|
||||
[hideThumbnail]="singleType"
|
||||
></assessments-list-item>
|
||||
<assessments-list-item [item]="item" [hideThumbnail]="singleType"></assessments-list-item>
|
||||
</ng-template>
|
||||
</stapps-simple-data-list>
|
||||
|
||||
@@ -1,27 +1,20 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<tree-list
|
||||
[items]="items"
|
||||
[singleType]="singleType"
|
||||
[groupingKey]="groupingKey"
|
||||
>
|
||||
<tree-list [items]="items" [singleType]="singleType" [groupingKey]="groupingKey">
|
||||
<ng-template let-item>
|
||||
<assessments-list-item
|
||||
[item]="item"
|
||||
[hideThumbnail]="singleType"
|
||||
></assessments-list-item>
|
||||
<assessments-list-item [item]="item" [hideThumbnail]="singleType"></assessments-list-item>
|
||||
</ng-template>
|
||||
</tree-list>
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
/*!
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {
|
||||
AfterViewInit,
|
||||
Component,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import {AfterViewInit, Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
||||
import {AssessmentsProvider} from '../assessments.provider';
|
||||
import {SCAssessment, SCCourseOfStudy} from '@openstapps/core';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
@@ -38,9 +32,7 @@ import {mapValues} from '../../../_helpers/collections/map-values';
|
||||
styleUrls: ['assessments-page.scss'],
|
||||
animations: [materialSharedAxisX],
|
||||
})
|
||||
export class AssessmentsPageComponent
|
||||
implements OnInit, AfterViewInit, OnDestroy
|
||||
{
|
||||
export class AssessmentsPageComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
assessments: Promise<
|
||||
Record<
|
||||
string,
|
||||
@@ -57,8 +49,7 @@ export class AssessmentsPageComponent
|
||||
|
||||
@ViewChild('segment') segmentView!: HTMLIonSegmentElement;
|
||||
|
||||
sharedAxisChoreographer: SharedAxisChoreographer<string> =
|
||||
new SharedAxisChoreographer<string>('', []);
|
||||
sharedAxisChoreographer: SharedAxisChoreographer<string> = new SharedAxisChoreographer<string>('', []);
|
||||
|
||||
constructor(
|
||||
readonly logger: NGXLogger,
|
||||
@@ -78,23 +69,19 @@ export class AssessmentsPageComponent
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.routingSubscription = this.dataRoutingService
|
||||
.itemSelectListener()
|
||||
.subscribe(thing => {
|
||||
void this.router.navigate(['assessments', 'detail', thing.uid], {
|
||||
queryParams: {
|
||||
token: this.activatedRoute.snapshot.queryParamMap.get('token'),
|
||||
},
|
||||
});
|
||||
this.routingSubscription = this.dataRoutingService.itemSelectListener().subscribe(thing => {
|
||||
void this.router.navigate(['assessments', 'detail', thing.uid], {
|
||||
queryParams: {
|
||||
token: this.activatedRoute.snapshot.queryParamMap.get('token'),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
this.activatedRoute.queryParams.subscribe(parameters => {
|
||||
try {
|
||||
this.assessments = this.assessmentsProvider
|
||||
.getAssessments(parameters.token)
|
||||
.then(assessments =>
|
||||
groupBy(assessments, it => it.courseOfStudy?.uid ?? 'unknown'),
|
||||
)
|
||||
.then(assessments => groupBy(assessments, it => it.courseOfStudy?.uid ?? 'unknown'))
|
||||
.then(it => {
|
||||
this.assessmentKeys = Object.keys(it);
|
||||
this.sharedAxisChoreographer = new SharedAxisChoreographer(
|
||||
@@ -102,8 +89,7 @@ export class AssessmentsPageComponent
|
||||
this.assessmentKeys,
|
||||
);
|
||||
if (this.segmentView) {
|
||||
this.segmentView.value =
|
||||
this.sharedAxisChoreographer.currentValue;
|
||||
this.segmentView.value = this.sharedAxisChoreographer.currentValue;
|
||||
}
|
||||
return it;
|
||||
})
|
||||
@@ -112,9 +98,7 @@ export class AssessmentsPageComponent
|
||||
assessments: group,
|
||||
courseOfStudy: this.dataProvider
|
||||
.get(uid, DataScope.Remote)
|
||||
.catch(
|
||||
() => group[0].courseOfStudy,
|
||||
) as Promise<SCCourseOfStudy>,
|
||||
.catch(() => group[0].courseOfStudy) as Promise<SCCourseOfStudy>,
|
||||
})),
|
||||
);
|
||||
} catch (error) {
|
||||
|
||||
@@ -34,14 +34,9 @@
|
||||
<div *ngIf="assessments | async as assessments">
|
||||
<ion-label
|
||||
class="ion-text-wrap"
|
||||
*ngIf="
|
||||
assessments[key].courseOfStudy | async as course;
|
||||
else defaultLabel
|
||||
"
|
||||
*ngIf="assessments[key].courseOfStudy | async as course; else defaultLabel"
|
||||
>
|
||||
{{ 'name' | thingTranslate: course }} ({{
|
||||
'academicDegree' | thingTranslate: course
|
||||
}})
|
||||
{{ 'name' | thingTranslate: course }} ({{ 'academicDegree' | thingTranslate: course }})
|
||||
</ion-label>
|
||||
</div>
|
||||
<ng-template #defaultLabel>
|
||||
@@ -58,9 +53,7 @@
|
||||
>
|
||||
<course-of-study-assessment
|
||||
[assessments]="items[sharedAxisChoreographer.currentValue].assessments"
|
||||
[courseOfStudy]="
|
||||
items[sharedAxisChoreographer.currentValue].courseOfStudy | async
|
||||
"
|
||||
[courseOfStudy]="items[sharedAxisChoreographer.currentValue].courseOfStudy | async"
|
||||
></course-of-study-assessment>
|
||||
</div>
|
||||
</ion-content>
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<ion-label [color]="passed ? undefined : 'danger'"
|
||||
>{{
|
||||
(_item.grade | isNumeric)
|
||||
? (_item.grade
|
||||
| numberLocalized: 'minimumFractionDigits:1,maximumFractionDigits:1')
|
||||
? (_item.grade | numberLocalized: 'minimumFractionDigits:1,maximumFractionDigits:1')
|
||||
: ''
|
||||
}}
|
||||
{{ 'status' | thingTranslate: _item | titlecase }},
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*!
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.column {
|
||||
@@ -28,7 +28,6 @@
|
||||
padding-left: 1px;
|
||||
}
|
||||
|
||||
|
||||
.super-assessments-list {
|
||||
// prevent the list from hijacking hover overlays
|
||||
z-index: -1;
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<section>
|
||||
<h3>{{ 'assessments.courseOfStudyAssessments.PROGRESS' | translate }}</h3>
|
||||
<p>
|
||||
{{ $any('grade' | propertyNameTranslate: 'assessment') | titlecase }}:
|
||||
{{
|
||||
grade | numberLocalized: 'minimumFractionDigits:1,maximumFractionDigits:1'
|
||||
}}
|
||||
{{ grade | numberLocalized: 'minimumFractionDigits:1,maximumFractionDigits:1' }}
|
||||
</p>
|
||||
<p>{{ 'ects' | propertyNameTranslate: 'assessment' }}: {{ ects }}</p>
|
||||
</section>
|
||||
@@ -28,10 +26,6 @@
|
||||
{{ 'assessments.courseOfStudyAssessments.ASSESSMENTS' | translate }}
|
||||
</h3>
|
||||
|
||||
<assessments-tree-list
|
||||
[items]="_assessments"
|
||||
[singleType]="true"
|
||||
[groupingKey]="'superAssessments'"
|
||||
>
|
||||
<assessments-tree-list [items]="_assessments" [singleType]="true" [groupingKey]="'superAssessments'">
|
||||
</assessments-tree-list>
|
||||
</section>
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
<!--
|
||||
~ Copyright (C) 2023 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<div class="centeredMessageContainer">
|
||||
<p>
|
||||
{{
|
||||
'auth.messages' + '.' + PROVIDER_TYPE + '.' + 'authorizing' | translate
|
||||
}}
|
||||
{{ 'auth.messages' + '.' + PROVIDER_TYPE + '.' + 'authorizing' | translate }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -14,12 +14,7 @@
|
||||
*/
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
import {
|
||||
CanActivate,
|
||||
NavigationExtras,
|
||||
Router,
|
||||
RouterStateSnapshot,
|
||||
} from '@angular/router';
|
||||
import {CanActivate, NavigationExtras, Router, RouterStateSnapshot} from '@angular/router';
|
||||
import {ActivatedProtectedRouteSnapshot} from './protected.routes';
|
||||
import {AuthHelperService} from './auth-helper.service';
|
||||
|
||||
@@ -29,18 +24,13 @@ import {AuthHelperService} from './auth-helper.service';
|
||||
export class AuthGuardService implements CanActivate {
|
||||
constructor(private authHelper: AuthHelperService, private router: Router) {}
|
||||
|
||||
public async canActivate(
|
||||
route: ActivatedProtectedRouteSnapshot,
|
||||
_state: RouterStateSnapshot,
|
||||
) {
|
||||
public async canActivate(route: ActivatedProtectedRouteSnapshot, _state: RouterStateSnapshot) {
|
||||
if (route.queryParamMap.get('token')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.authHelper
|
||||
.getProvider(route.data.authProvider)
|
||||
.getValidToken();
|
||||
await this.authHelper.getProvider(route.data.authProvider).getValidToken();
|
||||
} catch {
|
||||
const originNavigation = this.router.getCurrentNavigation();
|
||||
let extras: NavigationExtras = {};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* Copyright (C) 2023 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -27,28 +27,11 @@ import {HttpClientModule} from '@angular/common/http';
|
||||
|
||||
describe('AuthHelperService', () => {
|
||||
let authHelperService: AuthHelperService;
|
||||
const storageProviderSpy = jasmine.createSpyObj('StorageProvider', [
|
||||
'init',
|
||||
'get',
|
||||
'has',
|
||||
'put',
|
||||
'search',
|
||||
]);
|
||||
const translateServiceSpy = jasmine.createSpyObj('TranslateService', [
|
||||
'setDefaultLang',
|
||||
'use',
|
||||
]);
|
||||
const defaultAuthServiceMock = jasmine.createSpyObj('DefaultAuthService', [
|
||||
'init',
|
||||
'setupConfiguration',
|
||||
]);
|
||||
const paiaAuthServiceMock = jasmine.createSpyObj('PAIAAuthService', [
|
||||
'init',
|
||||
'setupConfiguration',
|
||||
]);
|
||||
const authHelperServiceMock = jasmine.createSpyObj('AuthHelperService', [
|
||||
'constructor',
|
||||
]);
|
||||
const storageProviderSpy = jasmine.createSpyObj('StorageProvider', ['init', 'get', 'has', 'put', 'search']);
|
||||
const translateServiceSpy = jasmine.createSpyObj('TranslateService', ['setDefaultLang', 'use']);
|
||||
const defaultAuthServiceMock = jasmine.createSpyObj('DefaultAuthService', ['init', 'setupConfiguration']);
|
||||
const paiaAuthServiceMock = jasmine.createSpyObj('PAIAAuthService', ['init', 'setupConfiguration']);
|
||||
const authHelperServiceMock = jasmine.createSpyObj('AuthHelperService', ['constructor']);
|
||||
const configProvider = jasmine.createSpyObj('ConfigProvider', {
|
||||
getAnyValue: {
|
||||
default: {
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2023 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
import {IPAIAAuthAction} from './paia/paia-auth-action';
|
||||
import {AuthActions, IAuthAction} from 'ionic-appauth';
|
||||
@@ -13,6 +28,7 @@ import {ConfigProvider} from '../config/config.provider';
|
||||
import {StorageProvider} from '../storage/storage.provider';
|
||||
import {DefaultAuthService} from './default-auth.service';
|
||||
import {PAIAAuthService} from './paia/paia-auth.service';
|
||||
|
||||
const AUTH_ORIGIN_PATH = 'stapps.auth.origin_path';
|
||||
|
||||
@Injectable({
|
||||
@@ -35,21 +51,14 @@ export class AuthHelperService {
|
||||
).default.endpoints.mapping;
|
||||
}
|
||||
|
||||
public getAuthMessage(
|
||||
provider: SCAuthorizationProviderType,
|
||||
action: IAuthAction | IPAIAAuthAction,
|
||||
) {
|
||||
public getAuthMessage(provider: SCAuthorizationProviderType, action: IAuthAction | IPAIAAuthAction) {
|
||||
let message: string | undefined;
|
||||
switch (action.action) {
|
||||
case AuthActions.SignInSuccess:
|
||||
message = this.translateService.instant(
|
||||
`auth.messages.${provider}.logged_in_success`,
|
||||
);
|
||||
message = this.translateService.instant(`auth.messages.${provider}.logged_in_success`);
|
||||
break;
|
||||
case AuthActions.SignOutSuccess:
|
||||
message = this.translateService.instant(
|
||||
`auth.messages.${provider}.logged_out_success`,
|
||||
);
|
||||
message = this.translateService.instant(`auth.messages.${provider}.logged_out_success`);
|
||||
break;
|
||||
}
|
||||
return message;
|
||||
@@ -63,19 +72,12 @@ export class AuthHelperService {
|
||||
};
|
||||
for (const key in this.userConfigurationMap) {
|
||||
user[key as keyof SCUserConfiguration] = JSONPath({
|
||||
path: this.userConfigurationMap[
|
||||
key as keyof SCUserConfiguration
|
||||
] as string,
|
||||
path: this.userConfigurationMap[key as keyof SCUserConfiguration] as string,
|
||||
json: userInfo,
|
||||
preventEval: true,
|
||||
})[0];
|
||||
}
|
||||
if (
|
||||
user.givenName &&
|
||||
user.givenName.length > 0 &&
|
||||
user.familyName &&
|
||||
user.familyName.length > 0
|
||||
) {
|
||||
if (user.givenName && user.givenName.length > 0 && user.familyName && user.familyName.length > 0) {
|
||||
user.name = `${user.givenName} ${user.familyName}`;
|
||||
}
|
||||
|
||||
@@ -106,9 +108,7 @@ export class AuthHelperService {
|
||||
/**
|
||||
* Provides appropriate auth service instance based on type (string) parameter
|
||||
*/
|
||||
getProvider(
|
||||
providerType: SCAuthorizationProviderType,
|
||||
): DefaultAuthService | PAIAAuthService {
|
||||
getProvider(providerType: SCAuthorizationProviderType): DefaultAuthService | PAIAAuthService {
|
||||
return providerType === 'paia'
|
||||
? (this.paiaAuth as PAIAAuthService)
|
||||
: (this.defaultAuth as DefaultAuthService);
|
||||
|
||||
@@ -15,10 +15,7 @@
|
||||
|
||||
import {AuthorizationServiceConfigurationJson} from '@openid/appauth';
|
||||
import {IAuthConfig} from 'ionic-appauth';
|
||||
import {
|
||||
SCAuthorizationProvider,
|
||||
SCAuthorizationProviderType,
|
||||
} from '@openstapps/core';
|
||||
import {SCAuthorizationProvider, SCAuthorizationProviderType} from '@openstapps/core';
|
||||
import {Capacitor} from '@capacitor/core';
|
||||
import {authPaths} from './auth-paths';
|
||||
import {environment} from '../../../environments/environment';
|
||||
@@ -71,18 +68,14 @@ function getRedirectUrl(routePath: string): string {
|
||||
let appHost: string;
|
||||
let appSchema: string;
|
||||
if (environment.production) {
|
||||
appSchema = Capacitor.isNativePlatform()
|
||||
? environment.custom_url_scheme
|
||||
: 'https';
|
||||
appSchema = Capacitor.isNativePlatform() ? environment.custom_url_scheme : 'https';
|
||||
appHost = environment.app_host;
|
||||
} else {
|
||||
appSchema = Capacitor.isNativePlatform()
|
||||
? environment.custom_url_scheme
|
||||
: window.location.protocol.split(':')[0];
|
||||
|
||||
appHost = Capacitor.isNativePlatform()
|
||||
? environment.app_host
|
||||
: window.location.host;
|
||||
appHost = Capacitor.isNativePlatform() ? environment.app_host : window.location.host;
|
||||
}
|
||||
return `${appSchema}://${appHost}/${routePath}`;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* Copyright (C) 2023 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -77,13 +77,9 @@ export abstract class AuthService implements IAuthService {
|
||||
|
||||
private _session: SessionObserver = new SessionObserver();
|
||||
|
||||
private _authSubjectV2 = new BehaviorSubject<IAuthAction>(
|
||||
AuthActionBuilder.Init(),
|
||||
);
|
||||
private _authSubjectV2 = new BehaviorSubject<IAuthAction>(AuthActionBuilder.Init());
|
||||
|
||||
private _tokenSubject = new BehaviorSubject<TokenResponse | undefined>(
|
||||
undefined,
|
||||
);
|
||||
private _tokenSubject = new BehaviorSubject<TokenResponse | undefined>(undefined);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private _userSubject = new BehaviorSubject<any>(undefined);
|
||||
@@ -107,10 +103,7 @@ export abstract class AuthService implements IAuthService {
|
||||
) {
|
||||
this.tokenHandler = new BaseTokenRequestHandler(requestor);
|
||||
this.userInfoHandler = new IonicUserInfoHandler(requestor);
|
||||
this.requestHandler = new IonicAuthorizationRequestHandler(
|
||||
browser,
|
||||
storage,
|
||||
);
|
||||
this.requestHandler = new IonicAuthorizationRequestHandler(browser, storage);
|
||||
this.endSessionHandler = new IonicEndSessionHandler(browser);
|
||||
}
|
||||
|
||||
@@ -211,9 +204,7 @@ export abstract class AuthService implements IAuthService {
|
||||
break;
|
||||
case AuthActions.LoadTokenFromStorageSuccess:
|
||||
this._tokenSubject.next(action.tokenResponse);
|
||||
this._authenticatedSubject.next(
|
||||
(action.tokenResponse as TokenResponse).isValid(0),
|
||||
);
|
||||
this._authenticatedSubject.next((action.tokenResponse as TokenResponse).isValid(0));
|
||||
this._initComplete.next(true);
|
||||
break;
|
||||
case AuthActions.RevokeTokensSuccess:
|
||||
@@ -245,9 +236,7 @@ export abstract class AuthService implements IAuthService {
|
||||
error: AuthorizationError | null,
|
||||
) {
|
||||
const codeVerifier: string | undefined =
|
||||
request.internal != undefined && this.authConfig.pkce
|
||||
? request.internal.code_verifier
|
||||
: undefined;
|
||||
request.internal != undefined && this.authConfig.pkce ? request.internal.code_verifier : undefined;
|
||||
|
||||
if (response != undefined) {
|
||||
this.requestAccessToken(response.code, codeVerifier);
|
||||
@@ -279,11 +268,10 @@ export abstract class AuthService implements IAuthService {
|
||||
};
|
||||
|
||||
const request: EndSessionRequest = new EndSessionRequest(requestJson);
|
||||
const returnedUrl: string | undefined =
|
||||
await this.endSessionHandler.performEndSessionRequest(
|
||||
await this.configuration,
|
||||
request,
|
||||
);
|
||||
const returnedUrl: string | undefined = await this.endSessionHandler.performEndSessionRequest(
|
||||
await this.configuration,
|
||||
request,
|
||||
);
|
||||
|
||||
//callback may come from showWindow or via another method
|
||||
if (returnedUrl != undefined) {
|
||||
@@ -295,10 +283,7 @@ export abstract class AuthService implements IAuthService {
|
||||
}
|
||||
}
|
||||
|
||||
protected async performAuthorizationRequest(
|
||||
authExtras?: StringMap,
|
||||
state?: string,
|
||||
): Promise<void> {
|
||||
protected async performAuthorizationRequest(authExtras?: StringMap, state?: string): Promise<void> {
|
||||
const requestJson: AuthorizationRequestJson = {
|
||||
response_type: AuthorizationRequest.RESPONSE_TYPE_CODE,
|
||||
client_id: this.authConfig.client_id,
|
||||
@@ -308,24 +293,14 @@ export abstract class AuthService implements IAuthService {
|
||||
state: state || undefined,
|
||||
};
|
||||
|
||||
const request = new AuthorizationRequest(
|
||||
requestJson,
|
||||
new DefaultCrypto(),
|
||||
this.authConfig.pkce,
|
||||
);
|
||||
const request = new AuthorizationRequest(requestJson, new DefaultCrypto(), this.authConfig.pkce);
|
||||
|
||||
if (this.authConfig.pkce) await request.setupCodeVerifier();
|
||||
|
||||
return this.requestHandler.performAuthorizationRequest(
|
||||
await this.configuration,
|
||||
request,
|
||||
);
|
||||
return this.requestHandler.performAuthorizationRequest(await this.configuration, request);
|
||||
}
|
||||
|
||||
protected async requestAccessToken(
|
||||
code: string,
|
||||
codeVerifier?: string,
|
||||
): Promise<void> {
|
||||
protected async requestAccessToken(code: string, codeVerifier?: string): Promise<void> {
|
||||
const requestJSON: TokenRequestJson = {
|
||||
grant_type: GRANT_TYPE_AUTHORIZATION_CODE,
|
||||
code: code,
|
||||
@@ -346,10 +321,7 @@ export abstract class AuthService implements IAuthService {
|
||||
await this.configuration,
|
||||
new TokenRequest(requestJSON),
|
||||
);
|
||||
await this.storage.setItem(
|
||||
TOKEN_RESPONSE_KEY,
|
||||
JSON.stringify(token.toJson()),
|
||||
);
|
||||
await this.storage.setItem(TOKEN_RESPONSE_KEY, JSON.stringify(token.toJson()));
|
||||
this.notifyActionListers(AuthActionBuilder.SignInSuccess(token));
|
||||
}
|
||||
|
||||
@@ -372,26 +344,19 @@ export abstract class AuthService implements IAuthService {
|
||||
if (!token.accessToken) {
|
||||
throw new Error('No Access Token Defined In Refresh Response');
|
||||
}
|
||||
await this.storage.setItem(
|
||||
TOKEN_RESPONSE_KEY,
|
||||
JSON.stringify(token.toJson()),
|
||||
);
|
||||
await this.storage.setItem(TOKEN_RESPONSE_KEY, JSON.stringify(token.toJson()));
|
||||
this.notifyActionListers(AuthActionBuilder.RefreshSuccess(token));
|
||||
}
|
||||
|
||||
protected async internalLoadTokenFromStorage() {
|
||||
let token: TokenResponse | undefined;
|
||||
const tokenResponseString: string | null = await this.storage.getItem(
|
||||
TOKEN_RESPONSE_KEY,
|
||||
);
|
||||
const tokenResponseString: string | null = await this.storage.getItem(TOKEN_RESPONSE_KEY);
|
||||
|
||||
if (tokenResponseString != undefined) {
|
||||
token = new TokenResponse(JSON.parse(tokenResponseString));
|
||||
|
||||
if (token) {
|
||||
return this.notifyActionListers(
|
||||
AuthActionBuilder.LoadTokenFromStorageSuccess(token),
|
||||
);
|
||||
return this.notifyActionListers(AuthActionBuilder.LoadTokenFromStorageSuccess(token));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,9 +402,7 @@ export abstract class AuthService implements IAuthService {
|
||||
|
||||
public async loadTokenFromStorage() {
|
||||
await this.internalLoadTokenFromStorage().catch(error => {
|
||||
this.notifyActionListers(
|
||||
AuthActionBuilder.LoadTokenFromStorageFailed(error),
|
||||
);
|
||||
this.notifyActionListers(AuthActionBuilder.LoadTokenFromStorageFailed(error));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -495,9 +458,7 @@ export abstract class AuthService implements IAuthService {
|
||||
});
|
||||
}
|
||||
|
||||
public async getValidToken(
|
||||
buffer: number = AUTH_EXPIRY_BUFFER,
|
||||
): Promise<TokenResponse> {
|
||||
public async getValidToken(buffer: number = AUTH_EXPIRY_BUFFER): Promise<TokenResponse> {
|
||||
if (this._tokenSubject.value) {
|
||||
if (!this._tokenSubject.value.isValid(buffer)) {
|
||||
await this.refreshToken();
|
||||
@@ -517,9 +478,7 @@ export abstract class AuthService implements IAuthService {
|
||||
* this will be removed in a future release
|
||||
* please use $ suffixed observers in future
|
||||
*/
|
||||
public addActionListener(
|
||||
function_: (action: IAuthAction) => void,
|
||||
): AuthObserver {
|
||||
public addActionListener(function_: (action: IAuthAction) => void): AuthObserver {
|
||||
const observer: AuthObserver = AuthObserver.Create(function_);
|
||||
this.addActionObserver(observer);
|
||||
return observer;
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {Requestor} from '@openid/appauth';
|
||||
import {CapacitorHttp, HttpHeaders, HttpResponse} from '@capacitor/core';
|
||||
import {XhrSettings} from 'ionic-appauth/lib/cordova';
|
||||
@@ -25,9 +40,7 @@ export class CapacitorRequestor extends Requestor {
|
||||
}
|
||||
|
||||
private async get<T>(url: string, headers: HttpHeaders) {
|
||||
return CapacitorHttp.get({url, headers}).then(
|
||||
(response: HttpResponse) => response.data as T,
|
||||
);
|
||||
return CapacitorHttp.get({url, headers}).then((response: HttpResponse) => response.data as T);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -59,9 +72,7 @@ export class CapacitorRequestor extends Requestor {
|
||||
}
|
||||
|
||||
private async delete<T>(url: string, headers: HttpHeaders) {
|
||||
return CapacitorHttp.delete({url, headers}).then(
|
||||
(response: HttpResponse) => response.data as T,
|
||||
);
|
||||
return CapacitorHttp.delete({url, headers}).then((response: HttpResponse) => response.data as T);
|
||||
}
|
||||
|
||||
private decodeURLSearchParams(parameters: string): Record<string, unknown> {
|
||||
@@ -69,9 +80,7 @@ export class CapacitorRequestor extends Requestor {
|
||||
return Object.fromEntries(
|
||||
[...searchParameters.keys()].map(k => [
|
||||
k,
|
||||
searchParameters.getAll(k).length === 1
|
||||
? searchParameters.get(k)
|
||||
: searchParameters.getAll(k),
|
||||
searchParameters.getAll(k).length === 1 ? searchParameters.get(k) : searchParameters.getAll(k),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* Copyright (C) 2023 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -27,17 +27,8 @@ import {IonicStorage} from 'ionic-appauth/lib';
|
||||
describe('AuthService', () => {
|
||||
let defaultAuthService: DefaultAuthService;
|
||||
let storageBackendSpy: jasmine.SpyObj<StorageBackend>;
|
||||
const storageProviderSpy = jasmine.createSpyObj('StorageProvider', [
|
||||
'init',
|
||||
'get',
|
||||
'has',
|
||||
'put',
|
||||
'search',
|
||||
]);
|
||||
const translateServiceSpy = jasmine.createSpyObj('TranslateService', [
|
||||
'setDefaultLang',
|
||||
'use',
|
||||
]);
|
||||
const storageProviderSpy = jasmine.createSpyObj('StorageProvider', ['init', 'get', 'has', 'put', 'search']);
|
||||
const translateServiceSpy = jasmine.createSpyObj('TranslateService', ['setDefaultLang', 'use']);
|
||||
|
||||
beforeEach(() => {
|
||||
storageBackendSpy = jasmine.createSpyObj('StorageBackend', ['getItem']);
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
import {AuthorizationRequestHandler} from '@openid/appauth';
|
||||
/*
|
||||
* Copyright (C) 2023 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {
|
||||
StorageBackend,
|
||||
Requestor,
|
||||
AuthorizationRequestHandler,
|
||||
AuthorizationServiceConfiguration,
|
||||
LocalStorageBackend,
|
||||
JQueryRequestor,
|
||||
LocalStorageBackend,
|
||||
Requestor,
|
||||
StorageBackend,
|
||||
TokenRequestHandler,
|
||||
} from '@openid/appauth';
|
||||
import {
|
||||
UserInfoHandler,
|
||||
EndSessionHandler,
|
||||
Browser,
|
||||
DefaultBrowser,
|
||||
AuthActionBuilder,
|
||||
} from 'ionic-appauth';
|
||||
import {AuthActionBuilder, Browser, DefaultBrowser, EndSessionHandler, UserInfoHandler} from 'ionic-appauth';
|
||||
import {ConfigProvider} from '../config/config.provider';
|
||||
import {SCAuthorizationProvider} from '@openstapps/core';
|
||||
import {getClientConfig, getEndpointsConfig} from './auth.provider.methods';
|
||||
@@ -46,8 +55,7 @@ export class DefaultAuthService extends AuthService {
|
||||
}
|
||||
|
||||
get configuration(): Promise<AuthorizationServiceConfiguration> {
|
||||
if (!this.localConfiguration)
|
||||
throw new Error('Local Configuration Not Defined');
|
||||
if (!this.localConfiguration) throw new Error('Local Configuration Not Defined');
|
||||
|
||||
return Promise.resolve(this.localConfiguration);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {Platform} from '@ionic/angular';
|
||||
import {DefaultBrowser} from 'ionic-appauth';
|
||||
import {CapacitorBrowser} from 'ionic-appauth/lib/capacitor';
|
||||
|
||||
export const browserFactory = (platform: Platform) => {
|
||||
return platform.is('capacitor')
|
||||
? new CapacitorBrowser()
|
||||
: new DefaultBrowser();
|
||||
return platform.is('capacitor') ? new CapacitorBrowser() : new DefaultBrowser();
|
||||
};
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {Platform} from '@ionic/angular';
|
||||
import {CapacitorRequestor} from '../capacitor-requestor';
|
||||
import {NgHttpService} from '../ng-http.service';
|
||||
|
||||
export const httpFactory = (platform: Platform, httpClient: HttpClient) => {
|
||||
return platform.is('capacitor')
|
||||
? new CapacitorRequestor()
|
||||
: new NgHttpService(httpClient);
|
||||
return platform.is('capacitor') ? new CapacitorRequestor() : new NgHttpService(httpClient);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {Platform} from '@ionic/angular';
|
||||
import {IonicStorage} from 'ionic-appauth/lib';
|
||||
import {SafeCapacitorSecureStorage} from '../../storage/capacitor-secure-storage';
|
||||
|
||||
export const storageFactory = (platform: Platform) => {
|
||||
return platform.is('capacitor')
|
||||
? new SafeCapacitorSecureStorage()
|
||||
: new IonicStorage();
|
||||
return platform.is('capacitor') ? new SafeCapacitorSecureStorage() : new IonicStorage();
|
||||
};
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
import {AuthCallbackPageComponent} from '../../../auth-callback/page/auth-callback-page.component';
|
||||
import {SCAuthorizationProviderType} from '@openstapps/core';
|
||||
@@ -12,11 +27,7 @@ import {AuthHelperService} from '../../../auth-helper.service';
|
||||
export class PAIAAuthCallbackPageComponent extends AuthCallbackPageComponent {
|
||||
PROVIDER_TYPE = 'paia' as SCAuthorizationProviderType;
|
||||
|
||||
constructor(
|
||||
navCtrl: NavController,
|
||||
router: Router,
|
||||
authHelper: AuthHelperService,
|
||||
) {
|
||||
constructor(navCtrl: NavController, router: Router, authHelper: AuthHelperService) {
|
||||
super(navCtrl, router, authHelper);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,10 +27,7 @@ import {
|
||||
import {Browser} from 'ionic-appauth';
|
||||
import {PAIAAuthorizationNotifier} from './paia-authorization-notifier';
|
||||
import {PAIAAuthorizationRequestResponse} from './authorization-request-response';
|
||||
import {
|
||||
PAIAAuthorizationResponse,
|
||||
PAIAAuthorizationResponseJson,
|
||||
} from './paia-authorization-response';
|
||||
import {PAIAAuthorizationResponse, PAIAAuthorizationResponseJson} from './paia-authorization-response';
|
||||
|
||||
/** key for authorization request. */
|
||||
const authorizationRequestKey = (handle: string) => {
|
||||
@@ -38,8 +35,7 @@ const authorizationRequestKey = (handle: string) => {
|
||||
};
|
||||
|
||||
/** key in local storage which represents the current authorization request. */
|
||||
const AUTHORIZATION_REQUEST_HANDLE_KEY =
|
||||
'appauth_current_authorization_request';
|
||||
const AUTHORIZATION_REQUEST_HANDLE_KEY = 'appauth_current_authorization_request';
|
||||
export const AUTHORIZATION_RESPONSE_KEY = 'auth_response';
|
||||
|
||||
export class PAIAAuthorizationRequestHandler {
|
||||
@@ -59,15 +55,9 @@ export class PAIAAuthorizationRequestHandler {
|
||||
): Promise<void> {
|
||||
const handle = this.generateRandom.generateRandom(10);
|
||||
await this.storage.setItem(AUTHORIZATION_REQUEST_HANDLE_KEY, handle);
|
||||
await this.storage.setItem(
|
||||
authorizationRequestKey(handle),
|
||||
JSON.stringify(await request.toJson()),
|
||||
);
|
||||
await this.storage.setItem(authorizationRequestKey(handle), JSON.stringify(await request.toJson()));
|
||||
const url = this.buildRequestUrl(configuration, request);
|
||||
const returnedUrl: string | undefined = await this.browser.showWindow(
|
||||
url,
|
||||
request.redirectUri,
|
||||
);
|
||||
const returnedUrl: string | undefined = await this.browser.showWindow(url, request.redirectUri);
|
||||
|
||||
// callback may come from showWindow or via another method
|
||||
if (typeof returnedUrl !== 'undefined') {
|
||||
@@ -86,9 +76,7 @@ export class PAIAAuthorizationRequestHandler {
|
||||
const request: AuthorizationRequest = this.getAuthorizationRequest(
|
||||
await this.storage.getItem(authorizationRequestKey(handle)),
|
||||
);
|
||||
const queryParameters = this.getQueryParams(
|
||||
await this.storage.getItem(AUTHORIZATION_RESPONSE_KEY),
|
||||
);
|
||||
const queryParameters = this.getQueryParams(await this.storage.getItem(AUTHORIZATION_RESPONSE_KEY));
|
||||
void this.removeItemsFromStorage(handle);
|
||||
|
||||
const state: string | undefined = queryParameters['state'];
|
||||
@@ -100,16 +88,12 @@ export class PAIAAuthorizationRequestHandler {
|
||||
|
||||
return <PAIAAuthorizationRequestResponse>{
|
||||
request: request, // request
|
||||
response: !error
|
||||
? this.getAuthorizationResponse(queryParameters)
|
||||
: undefined,
|
||||
response: !error ? this.getAuthorizationResponse(queryParameters) : undefined,
|
||||
error: error ? this.getAuthorizationError(queryParameters) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
private getAuthorizationRequest(
|
||||
authRequest: string | null,
|
||||
): AuthorizationRequest {
|
||||
private getAuthorizationRequest(authRequest: string | null): AuthorizationRequest {
|
||||
if (authRequest == undefined) {
|
||||
throw new Error('No Auth Request Available');
|
||||
}
|
||||
@@ -117,9 +101,7 @@ export class PAIAAuthorizationRequestHandler {
|
||||
return new AuthorizationRequest(JSON.parse(authRequest));
|
||||
}
|
||||
|
||||
private getAuthorizationError(
|
||||
queryParameters: StringMap,
|
||||
): AuthorizationError {
|
||||
private getAuthorizationError(queryParameters: StringMap): AuthorizationError {
|
||||
const authorizationErrorJSON: AuthorizationErrorJson = {
|
||||
error: queryParameters['error'],
|
||||
error_description: queryParameters['error_description'],
|
||||
@@ -129,9 +111,7 @@ export class PAIAAuthorizationRequestHandler {
|
||||
return new AuthorizationError(authorizationErrorJSON);
|
||||
}
|
||||
|
||||
private getAuthorizationResponse(
|
||||
queryParameters: StringMap,
|
||||
): PAIAAuthorizationResponse {
|
||||
private getAuthorizationResponse(queryParameters: StringMap): PAIAAuthorizationResponse {
|
||||
const authorizationResponseJSON: PAIAAuthorizationResponseJson = {
|
||||
code: queryParameters['code'],
|
||||
patron: queryParameters['patron'],
|
||||
@@ -159,9 +139,7 @@ export class PAIAAuthorizationRequestHandler {
|
||||
}
|
||||
}
|
||||
|
||||
setAuthorizationNotifier(
|
||||
notifier: PAIAAuthorizationNotifier,
|
||||
): PAIAAuthorizationRequestHandler {
|
||||
setAuthorizationNotifier(notifier: PAIAAuthorizationNotifier): PAIAAuthorizationRequestHandler {
|
||||
this.notifier = notifier;
|
||||
return this;
|
||||
}
|
||||
@@ -169,9 +147,7 @@ export class PAIAAuthorizationRequestHandler {
|
||||
completeAuthorizationRequestIfPossible(): Promise<void> {
|
||||
// call complete authorization if possible to see there might
|
||||
// be a response that needs to be delivered.
|
||||
console.log(
|
||||
`Checking to see if there is an authorization response to be delivered.`,
|
||||
);
|
||||
console.log(`Checking to see if there is an authorization response to be delivered.`);
|
||||
if (!this.notifier) {
|
||||
console.log(`Notifier is not present on AuthorizationRequest handler.
|
||||
No delivery of result will be possible`);
|
||||
@@ -181,11 +157,7 @@ export class PAIAAuthorizationRequestHandler {
|
||||
console.log(`No result is available yet.`);
|
||||
}
|
||||
if (result && this.notifier) {
|
||||
this.notifier.onAuthorizationComplete(
|
||||
result.request,
|
||||
result.response,
|
||||
result.error,
|
||||
);
|
||||
this.notifier.onAuthorizationComplete(result.request, result.response, result.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -193,10 +165,7 @@ export class PAIAAuthorizationRequestHandler {
|
||||
/**
|
||||
* A utility method to be able to build the authorization request URL.
|
||||
*/
|
||||
protected buildRequestUrl(
|
||||
configuration: AuthorizationServiceConfiguration,
|
||||
request: AuthorizationRequest,
|
||||
) {
|
||||
protected buildRequestUrl(configuration: AuthorizationServiceConfiguration, request: AuthorizationRequest) {
|
||||
// build the query string
|
||||
// coerce to any type for convenience
|
||||
const requestMap: StringMap = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 StApps
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -33,9 +33,7 @@ export class PAIAAuthActionBuilder extends AuthActionBuilder {
|
||||
return AuthActionBuilder.SignOutFailed(error) as IPAIAAuthAction;
|
||||
}
|
||||
|
||||
public static RefreshSuccess(
|
||||
tokenResponse: PAIATokenResponse,
|
||||
): IPAIAAuthAction {
|
||||
public static RefreshSuccess(tokenResponse: PAIATokenResponse): IPAIAAuthAction {
|
||||
return AuthActionBuilder.RefreshSuccess(tokenResponse) as IPAIAAuthAction;
|
||||
}
|
||||
|
||||
@@ -43,9 +41,7 @@ export class PAIAAuthActionBuilder extends AuthActionBuilder {
|
||||
return AuthActionBuilder.RefreshFailed(error) as IPAIAAuthAction;
|
||||
}
|
||||
|
||||
public static SignInSuccess(
|
||||
tokenResponse: PAIATokenResponse,
|
||||
): IPAIAAuthAction {
|
||||
public static SignInSuccess(tokenResponse: PAIATokenResponse): IPAIAAuthAction {
|
||||
return AuthActionBuilder.SignInSuccess(tokenResponse) as IPAIAAuthAction;
|
||||
}
|
||||
|
||||
@@ -53,18 +49,12 @@ export class PAIAAuthActionBuilder extends AuthActionBuilder {
|
||||
return AuthActionBuilder.SignInFailed(error) as IPAIAAuthAction;
|
||||
}
|
||||
|
||||
public static LoadTokenFromStorageSuccess(
|
||||
tokenResponse: PAIATokenResponse,
|
||||
): IPAIAAuthAction {
|
||||
return AuthActionBuilder.LoadTokenFromStorageSuccess(
|
||||
tokenResponse,
|
||||
) as IPAIAAuthAction;
|
||||
public static LoadTokenFromStorageSuccess(tokenResponse: PAIATokenResponse): IPAIAAuthAction {
|
||||
return AuthActionBuilder.LoadTokenFromStorageSuccess(tokenResponse) as IPAIAAuthAction;
|
||||
}
|
||||
|
||||
public static LoadTokenFromStorageFailed(error: Error): IPAIAAuthAction {
|
||||
return AuthActionBuilder.LoadTokenFromStorageFailed(
|
||||
error,
|
||||
) as IPAIAAuthAction;
|
||||
return AuthActionBuilder.LoadTokenFromStorageFailed(error) as IPAIAAuthAction;
|
||||
}
|
||||
|
||||
public static RevokeTokensSuccess(): IPAIAAuthAction {
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2023 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {
|
||||
AuthorizationError,
|
||||
AuthorizationRequest,
|
||||
@@ -57,13 +72,9 @@ export class PAIAAuthService {
|
||||
|
||||
private _authSubject: AuthSubject = new AuthSubject();
|
||||
|
||||
private _authSubjectV2 = new BehaviorSubject<IPAIAAuthAction>(
|
||||
PAIAAuthActionBuilder.Init(),
|
||||
);
|
||||
private _authSubjectV2 = new BehaviorSubject<IPAIAAuthAction>(PAIAAuthActionBuilder.Init());
|
||||
|
||||
private _tokenSubject = new BehaviorSubject<PAIATokenResponse | undefined>(
|
||||
undefined,
|
||||
);
|
||||
private _tokenSubject = new BehaviorSubject<PAIATokenResponse | undefined>(undefined);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private _userSubject = new BehaviorSubject<any>(undefined);
|
||||
@@ -131,8 +142,7 @@ export class PAIAAuthService {
|
||||
}
|
||||
|
||||
get configuration(): Promise<AuthorizationServiceConfiguration> {
|
||||
if (!this.localConfiguration)
|
||||
throw new Error('Local Configuration Not Defined');
|
||||
if (!this.localConfiguration) throw new Error('Local Configuration Not Defined');
|
||||
|
||||
return Promise.resolve(this.localConfiguration);
|
||||
}
|
||||
@@ -148,9 +158,7 @@ export class PAIAAuthService {
|
||||
paia: SCAuthorizationProvider;
|
||||
};
|
||||
this.authConfig = getClientConfig('paia', authConfig);
|
||||
this.localConfiguration = new AuthorizationServiceConfiguration(
|
||||
getEndpointsConfig('paia', authConfig),
|
||||
);
|
||||
this.localConfiguration = new AuthorizationServiceConfiguration(getEndpointsConfig('paia', authConfig));
|
||||
}
|
||||
|
||||
protected notifyActionListers(action: IPAIAAuthAction) {
|
||||
@@ -175,9 +183,7 @@ export class PAIAAuthService {
|
||||
break;
|
||||
case AuthActions.LoadTokenFromStorageSuccess:
|
||||
this._tokenSubject.next(action.tokenResponse);
|
||||
this._authenticatedSubject.next(
|
||||
(action.tokenResponse as TokenResponse).isValid(0),
|
||||
);
|
||||
this._authenticatedSubject.next((action.tokenResponse as TokenResponse).isValid(0));
|
||||
this._initComplete.next(true);
|
||||
break;
|
||||
case AuthActions.RevokeTokensSuccess:
|
||||
@@ -209,9 +215,7 @@ export class PAIAAuthService {
|
||||
error: AuthorizationError | null,
|
||||
) {
|
||||
const codeVerifier: string | undefined =
|
||||
request.internal != undefined && this.authConfig.pkce
|
||||
? request.internal.code_verifier
|
||||
: undefined;
|
||||
request.internal != undefined && this.authConfig.pkce ? request.internal.code_verifier : undefined;
|
||||
|
||||
if (response != undefined) {
|
||||
this.requestAccessToken(response.code, response.patron, codeVerifier);
|
||||
@@ -228,10 +232,7 @@ export class PAIAAuthService {
|
||||
return this.requestHandler.completeAuthorizationRequestIfPossible();
|
||||
}
|
||||
|
||||
protected async performAuthorizationRequest(
|
||||
authExtras?: StringMap,
|
||||
state?: string,
|
||||
): Promise<void> {
|
||||
protected async performAuthorizationRequest(authExtras?: StringMap, state?: string): Promise<void> {
|
||||
const requestJson: AuthorizationRequestJson = {
|
||||
response_type: AuthorizationRequest.RESPONSE_TYPE_CODE,
|
||||
client_id: this.authConfig.client_id,
|
||||
@@ -241,25 +242,14 @@ export class PAIAAuthService {
|
||||
state: state || undefined,
|
||||
};
|
||||
|
||||
const request = new AuthorizationRequest(
|
||||
requestJson,
|
||||
new DefaultCrypto(),
|
||||
this.authConfig.pkce,
|
||||
);
|
||||
const request = new AuthorizationRequest(requestJson, new DefaultCrypto(), this.authConfig.pkce);
|
||||
|
||||
if (this.authConfig.pkce) await request.setupCodeVerifier();
|
||||
|
||||
return this.requestHandler.performAuthorizationRequest(
|
||||
await this.configuration,
|
||||
request,
|
||||
);
|
||||
return this.requestHandler.performAuthorizationRequest(await this.configuration, request);
|
||||
}
|
||||
|
||||
protected async requestAccessToken(
|
||||
code: string,
|
||||
patron: string,
|
||||
codeVerifier?: string,
|
||||
): Promise<void> {
|
||||
protected async requestAccessToken(code: string, patron: string, codeVerifier?: string): Promise<void> {
|
||||
const requestJSON: PAIATokenRequestJson = {
|
||||
code: code,
|
||||
patron: patron,
|
||||
@@ -270,15 +260,11 @@ export class PAIAAuthService {
|
||||
: {},
|
||||
};
|
||||
|
||||
const token: PAIATokenResponse =
|
||||
await this.tokenHandler.performTokenRequest(
|
||||
await this.configuration,
|
||||
new PAIATokenRequest(requestJSON),
|
||||
);
|
||||
await this.storage.setItem(
|
||||
TOKEN_RESPONSE_KEY,
|
||||
JSON.stringify(token.toJson()),
|
||||
const token: PAIATokenResponse = await this.tokenHandler.performTokenRequest(
|
||||
await this.configuration,
|
||||
new PAIATokenRequest(requestJSON),
|
||||
);
|
||||
await this.storage.setItem(TOKEN_RESPONSE_KEY, JSON.stringify(token.toJson()));
|
||||
this.notifyActionListers(PAIAAuthActionBuilder.SignInSuccess(token));
|
||||
}
|
||||
|
||||
@@ -297,17 +283,13 @@ export class PAIAAuthService {
|
||||
|
||||
protected async internalLoadTokenFromStorage() {
|
||||
let token: PAIATokenResponse | undefined;
|
||||
const tokenResponseString: string | null = await this.storage.getItem(
|
||||
TOKEN_RESPONSE_KEY,
|
||||
);
|
||||
const tokenResponseString: string | null = await this.storage.getItem(TOKEN_RESPONSE_KEY);
|
||||
|
||||
if (tokenResponseString != undefined) {
|
||||
token = new PAIATokenResponse(JSON.parse(tokenResponseString));
|
||||
|
||||
if (token) {
|
||||
return this.notifyActionListers(
|
||||
PAIAAuthActionBuilder.LoadTokenFromStorageSuccess(token),
|
||||
);
|
||||
return this.notifyActionListers(PAIAAuthActionBuilder.LoadTokenFromStorageSuccess(token));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,9 +302,7 @@ export class PAIAAuthService {
|
||||
await this.configuration,
|
||||
this._tokenSubject.value,
|
||||
);
|
||||
this.notifyActionListers(
|
||||
PAIAAuthActionBuilder.LoadUserInfoSuccess(userInfo),
|
||||
);
|
||||
this.notifyActionListers(PAIAAuthActionBuilder.LoadUserInfoSuccess(userInfo));
|
||||
} else {
|
||||
throw new Error('No Token Available');
|
||||
}
|
||||
@@ -330,9 +310,7 @@ export class PAIAAuthService {
|
||||
|
||||
public async loadTokenFromStorage() {
|
||||
await this.internalLoadTokenFromStorage().catch(error => {
|
||||
this.notifyActionListers(
|
||||
PAIAAuthActionBuilder.LoadTokenFromStorageFailed(error),
|
||||
);
|
||||
this.notifyActionListers(PAIAAuthActionBuilder.LoadTokenFromStorageFailed(error));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -354,9 +332,7 @@ export class PAIAAuthService {
|
||||
});
|
||||
}
|
||||
|
||||
public async getValidToken(
|
||||
buffer: number = AUTH_EXPIRY_BUFFER,
|
||||
): Promise<PAIATokenResponse> {
|
||||
public async getValidToken(buffer: number = AUTH_EXPIRY_BUFFER): Promise<PAIATokenResponse> {
|
||||
if (this._tokenSubject.value && this._tokenSubject.value.isValid(buffer)) {
|
||||
return this._tokenSubject.value;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 StApps
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -33,9 +33,7 @@ export class PAIAUserInfoHandler implements UserInfoHandler {
|
||||
url: `${configuration.userInfoEndpoint}/${token.patron}`,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `${
|
||||
token.tokenType == 'bearer' ? 'Bearer' : token.tokenType
|
||||
} ${token.accessToken}`,
|
||||
Authorization: `${token.tokenType == 'bearer' ? 'Bearer' : token.tokenType} ${token.accessToken}`,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 StApps
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -58,9 +58,7 @@ export class PAIATokenRequestHandler {
|
||||
configuration: AuthorizationServiceConfiguration,
|
||||
request: PAIATokenRequest,
|
||||
): Promise<PAIATokenResponse> {
|
||||
const tokenResponse = this.requestor.xhr<
|
||||
PAIATokenResponseJson | TokenErrorJson
|
||||
>({
|
||||
const tokenResponse = this.requestor.xhr<PAIATokenResponseJson | TokenErrorJson>({
|
||||
url: configuration.tokenEndpoint,
|
||||
method: 'POST',
|
||||
data: {
|
||||
@@ -77,9 +75,7 @@ export class PAIATokenRequestHandler {
|
||||
return tokenResponse.then(response => {
|
||||
return this.isTokenResponse(response)
|
||||
? new PAIATokenResponse(response)
|
||||
: Promise.reject<PAIATokenResponse>(
|
||||
new AppAuthError(response.error, new TokenError(response)),
|
||||
);
|
||||
: Promise.reject<PAIATokenResponse>(new AppAuthError(response.error, new TokenError(response)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -19,10 +19,7 @@
|
||||
export function hashStringToInt(string_: string): number {
|
||||
return [...string_].reduce(
|
||||
(accumulator, current) =>
|
||||
(current.codePointAt(0) ?? 0) +
|
||||
(accumulator << 6) +
|
||||
(accumulator << 16) -
|
||||
accumulator,
|
||||
(current.codePointAt(0) ?? 0) + (accumulator << 6) + (accumulator << 16) - accumulator,
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {Injectable, OnDestroy} from '@angular/core';
|
||||
@@ -51,12 +51,10 @@ export class ScheduleSyncService implements OnDestroy {
|
||||
) {}
|
||||
|
||||
init() {
|
||||
this.scheduleProvider.uuids$
|
||||
.pipe(filter(uuids => uuids?.length > 0))
|
||||
.subscribe(uuids => {
|
||||
this.uuids = uuids;
|
||||
void this.syncNativeCalendar();
|
||||
});
|
||||
this.scheduleProvider.uuids$.pipe(filter(uuids => uuids?.length > 0)).subscribe(uuids => {
|
||||
this.uuids = uuids;
|
||||
void this.syncNativeCalendar();
|
||||
});
|
||||
}
|
||||
|
||||
uuids: SCUuid[];
|
||||
@@ -72,10 +70,7 @@ export class ScheduleSyncService implements OnDestroy {
|
||||
}
|
||||
|
||||
private async isNotificationsEnabled(): Promise<boolean> {
|
||||
return getCalendarSetting(
|
||||
this.storageProvider,
|
||||
CALENDAR_NOTIFICATIONS_ENABLED_KEY,
|
||||
);
|
||||
return getCalendarSetting(this.storageProvider, CALENDAR_NOTIFICATIONS_ENABLED_KEY);
|
||||
}
|
||||
|
||||
async enable() {
|
||||
@@ -84,10 +79,7 @@ export class ScheduleSyncService implements OnDestroy {
|
||||
await BackgroundFetch.stop();
|
||||
|
||||
if (
|
||||
[
|
||||
this.isSyncEnabled.bind(this),
|
||||
this.isNotificationsEnabled.bind(this),
|
||||
].some(async it => await it())
|
||||
[this.isSyncEnabled.bind(this), this.isNotificationsEnabled.bind(this)].some(async it => await it())
|
||||
) {
|
||||
const status = await BackgroundFetch.configure(
|
||||
{
|
||||
@@ -95,10 +87,7 @@ export class ScheduleSyncService implements OnDestroy {
|
||||
requiredNetworkType: 1,
|
||||
},
|
||||
async taskId => {
|
||||
await Promise.all([
|
||||
this.postDifferencesNotification(),
|
||||
this.syncNativeCalendar(),
|
||||
]);
|
||||
await Promise.all([this.postDifferencesNotification(), this.syncNativeCalendar()]);
|
||||
|
||||
await BackgroundFetch.finish(taskId);
|
||||
},
|
||||
@@ -110,9 +99,7 @@ export class ScheduleSyncService implements OnDestroy {
|
||||
'The user explicitly disabled background behavior for this app or for the whole system.',
|
||||
);
|
||||
} else if (status === BackgroundFetch.STATUS_RESTRICTED) {
|
||||
console.error(
|
||||
'Background updates are unavailable and the user cannot enable them again.',
|
||||
);
|
||||
console.error('Background updates are unavailable and the user cannot enable them again.');
|
||||
}
|
||||
} else {
|
||||
console.info('Starting background fetch.');
|
||||
@@ -122,14 +109,10 @@ export class ScheduleSyncService implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
async getDifferences(): Promise<
|
||||
ChangesOf<SCDateSeries, DateSeriesRelevantData>[]
|
||||
> {
|
||||
async getDifferences(): Promise<ChangesOf<SCDateSeries, DateSeriesRelevantData>[]> {
|
||||
const partialEvents = this.scheduleProvider.partialEvents$.getValue();
|
||||
|
||||
const result = (
|
||||
await this.scheduleProvider.getDateSeries(partialEvents.map(it => it.uid))
|
||||
).dates;
|
||||
const result = (await this.scheduleProvider.getDateSeries(partialEvents.map(it => it.uid))).dates;
|
||||
|
||||
return result
|
||||
.map(it => ({
|
||||
@@ -148,15 +131,11 @@ export class ScheduleSyncService implements OnDestroy {
|
||||
}));
|
||||
}
|
||||
|
||||
private formatChanges(
|
||||
changes: ChangesOf<SCDateSeries, DateSeriesRelevantData>,
|
||||
): string[] {
|
||||
private formatChanges(changes: ChangesOf<SCDateSeries, DateSeriesRelevantData>): string[] {
|
||||
return changes.changes.map(
|
||||
change =>
|
||||
`${
|
||||
this.translator.translator.translatedPropertyNames<SCDateSeries>(
|
||||
SCThingType.DateSeries,
|
||||
)?.[change]
|
||||
this.translator.translator.translatedPropertyNames<SCDateSeries>(SCThingType.DateSeries)?.[change]
|
||||
}: ${formatRelevantKeys[change](
|
||||
changes.new[change] as never,
|
||||
this.dateFormatPipe,
|
||||
@@ -168,8 +147,7 @@ export class ScheduleSyncService implements OnDestroy {
|
||||
async syncNativeCalendar() {
|
||||
if (!(await this.isSyncEnabled())) return;
|
||||
|
||||
const dateSeries = (await this.scheduleProvider.getDateSeries(this.uuids))
|
||||
.dates;
|
||||
const dateSeries = (await this.scheduleProvider.getDateSeries(this.uuids)).dates;
|
||||
|
||||
const events = dateSeries.flatMap(event =>
|
||||
toICal(event, this.translator.translator, {
|
||||
@@ -184,9 +162,7 @@ export class ScheduleSyncService implements OnDestroy {
|
||||
async postDifferencesNotification() {
|
||||
if (!(await this.isNotificationsEnabled())) return;
|
||||
|
||||
const differences = (await this.getDifferences()).filter(
|
||||
it => it.changes.length > 0,
|
||||
);
|
||||
const differences = (await this.getDifferences()).filter(it => it.changes.length > 0);
|
||||
if (differences.length === 0) return;
|
||||
|
||||
if (Capacitor.isNativePlatform()) {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {
|
||||
@@ -68,9 +68,7 @@ export class AddEventReviewModalComponent implements OnInit {
|
||||
});
|
||||
|
||||
this.iCalEvents = this.dateSeries.map(event => ({
|
||||
title:
|
||||
this.translator.translator.translatedAccess(event).event.name() ??
|
||||
'error',
|
||||
title: this.translator.translator.translatedAccess(event).event.name() ?? 'error',
|
||||
events: toICal(event, this.translator.translator, {
|
||||
allowRRuleExceptions: true,
|
||||
excludeCancelledEvents: false,
|
||||
@@ -81,9 +79,7 @@ export class AddEventReviewModalComponent implements OnInit {
|
||||
|
||||
async toCalendar() {
|
||||
await Dialog.confirm({
|
||||
title: this.translateService.instant(
|
||||
'schedule.toCalendar.reviewModal.dialogs.toCalendarConfirm.TITLE',
|
||||
),
|
||||
title: this.translateService.instant('schedule.toCalendar.reviewModal.dialogs.toCalendarConfirm.TITLE'),
|
||||
message: this.translateService.instant(
|
||||
'schedule.toCalendar.reviewModal.dialogs.toCalendarConfirm.DESCRIPTION',
|
||||
),
|
||||
@@ -98,15 +94,7 @@ export class AddEventReviewModalComponent implements OnInit {
|
||||
|
||||
async download() {
|
||||
const blob = new Blob(
|
||||
[
|
||||
serializeICal(
|
||||
getICalExport(
|
||||
this.dateSeries,
|
||||
this.translator.translator,
|
||||
this.includeCancelled,
|
||||
),
|
||||
),
|
||||
],
|
||||
[serializeICal(getICalExport(this.dateSeries, this.translator.translator, this.includeCancelled))],
|
||||
{
|
||||
type: 'text/calendar',
|
||||
},
|
||||
@@ -115,11 +103,7 @@ export class AddEventReviewModalComponent implements OnInit {
|
||||
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `${
|
||||
this.dateSeries.length === 1
|
||||
? this.dateSeries[0].event.name
|
||||
: 'stapps_calendar'
|
||||
}.ics`;
|
||||
a.download = `${this.dateSeries.length === 1 ? this.dateSeries[0].event.name : 'stapps_calendar'}.ics`;
|
||||
a.click();
|
||||
}
|
||||
|
||||
@@ -128,15 +112,7 @@ export class AddEventReviewModalComponent implements OnInit {
|
||||
|
||||
if (info.platform === 'web') {
|
||||
const blob = new Blob(
|
||||
[
|
||||
serializeICal(
|
||||
getICalExport(
|
||||
this.dateSeries,
|
||||
this.translator.translator,
|
||||
this.includeCancelled,
|
||||
),
|
||||
),
|
||||
],
|
||||
[serializeICal(getICalExport(this.dateSeries, this.translator.translator, this.includeCancelled))],
|
||||
{
|
||||
type: 'text/calendar',
|
||||
},
|
||||
@@ -144,27 +120,19 @@ export class AddEventReviewModalComponent implements OnInit {
|
||||
const file = new File([blob], 'calendar.ics', {type: blob.type});
|
||||
const shareData: NewShareData = {
|
||||
files: [file],
|
||||
title: this.translateService.instant(
|
||||
'schedule.toCalendar.reviewModal.shareData.TITLE',
|
||||
),
|
||||
text: this.translateService.instant(
|
||||
'schedule.toCalendar.reviewModal.shareData.TEXT',
|
||||
),
|
||||
title: this.translateService.instant('schedule.toCalendar.reviewModal.shareData.TITLE'),
|
||||
text: this.translateService.instant('schedule.toCalendar.reviewModal.shareData.TEXT'),
|
||||
};
|
||||
|
||||
if (!(navigator as unknown as NewShareNavigator).canShare) {
|
||||
return Dialog.alert({
|
||||
title: this.translateService.instant(
|
||||
'schedule.toCalendar.reviewModal.dialogs.cannotShare.TITLE',
|
||||
),
|
||||
title: this.translateService.instant('schedule.toCalendar.reviewModal.dialogs.cannotShare.TITLE'),
|
||||
message: this.translateService.instant(
|
||||
'schedule.toCalendar.reviewModal.dialogs.cannotShare.DESCRIPTION',
|
||||
),
|
||||
});
|
||||
}
|
||||
console.log(
|
||||
(navigator as unknown as NewShareNavigator).canShare(shareData),
|
||||
);
|
||||
console.log((navigator as unknown as NewShareNavigator).canShare(shareData));
|
||||
|
||||
if (!(navigator as unknown as NewShareNavigator).canShare(shareData)) {
|
||||
return Dialog.alert({
|
||||
@@ -181,9 +149,7 @@ export class AddEventReviewModalComponent implements OnInit {
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return Dialog.alert({
|
||||
title: this.translateService.instant(
|
||||
'schedule.toCalendar.reviewModal.dialogs.failedShare.TITLE',
|
||||
),
|
||||
title: this.translateService.instant('schedule.toCalendar.reviewModal.dialogs.failedShare.TITLE'),
|
||||
message: this.translateService.instant(
|
||||
'schedule.toCalendar.reviewModal.dialogs.failedShare.DESCRIPTION',
|
||||
),
|
||||
@@ -194,32 +160,20 @@ export class AddEventReviewModalComponent implements OnInit {
|
||||
path: `${
|
||||
this.dateSeries.length === 1
|
||||
? this.dateSeries[0].event.name
|
||||
: this.translateService.instant(
|
||||
'schedule.toCalendar.reviewModal.shareData.FILE_TYPE',
|
||||
)
|
||||
: this.translateService.instant('schedule.toCalendar.reviewModal.shareData.FILE_TYPE')
|
||||
}.ics`,
|
||||
data: serializeICal(
|
||||
getICalExport(
|
||||
this.dateSeries,
|
||||
this.translator.translator,
|
||||
this.includeCancelled,
|
||||
),
|
||||
getICalExport(this.dateSeries, this.translator.translator, this.includeCancelled),
|
||||
),
|
||||
encoding: Encoding.UTF8,
|
||||
directory: Directory.Cache,
|
||||
});
|
||||
|
||||
await Share.share({
|
||||
title: this.translateService.instant(
|
||||
'schedule.toCalendar.reviewModal.shareData.TITLE',
|
||||
),
|
||||
text: this.translateService.instant(
|
||||
'schedule.toCalendar.reviewModal.shareData.TEXT',
|
||||
),
|
||||
title: this.translateService.instant('schedule.toCalendar.reviewModal.shareData.TITLE'),
|
||||
text: this.translateService.instant('schedule.toCalendar.reviewModal.shareData.TEXT'),
|
||||
url: result.uri,
|
||||
dialogTitle: this.translateService.instant(
|
||||
'schedule.toCalendar.reviewModal.shareData.TITLE',
|
||||
),
|
||||
dialogTitle: this.translateService.instant('schedule.toCalendar.reviewModal.shareData.TITLE'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<div>
|
||||
<ion-card-header>
|
||||
<ion-card-title>{{
|
||||
'schedule.toCalendar.reviewModal.TITLE' | translate
|
||||
}}</ion-card-title>
|
||||
<ion-card-title>{{ 'schedule.toCalendar.reviewModal.TITLE' | translate }}</ion-card-title>
|
||||
<ion-button fill="clear" (click)="dismissAction()">
|
||||
<ion-label>{{ 'modal.DISMISS' | translate }}</ion-label>
|
||||
</ion-button>
|
||||
@@ -52,9 +50,7 @@
|
||||
|
||||
<div class="horizontal-flex">
|
||||
<ion-item lines="none">
|
||||
<ion-label>{{
|
||||
'schedule.toCalendar.reviewModal.INCLUDE_CANCELLED' | translate
|
||||
}}</ion-label>
|
||||
<ion-label>{{ 'schedule.toCalendar.reviewModal.INCLUDE_CANCELLED' | translate }}</ion-label>
|
||||
<ion-checkbox [(ngModel)]="includeCancelled" slot="end"></ion-checkbox>
|
||||
</ion-item>
|
||||
</div>
|
||||
@@ -63,11 +59,7 @@
|
||||
{{ 'share' | translate }}
|
||||
<ion-icon slot="end" md="share" ios="ios_share"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button
|
||||
fill="outline"
|
||||
(click)="download()"
|
||||
*ngIf="isWeb; else exportButton"
|
||||
>
|
||||
<ion-button fill="outline" (click)="download()" *ngIf="isWeb; else exportButton">
|
||||
{{ 'schedule.toCalendar.reviewModal.DOWNLOAD' | translate }}
|
||||
<ion-icon slot="end" name="download"></ion-icon>
|
||||
</ion-button>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {Calendar} from '@awesome-cordova-plugins/calendar/ngx';
|
||||
@@ -22,9 +22,7 @@ import {CalendarInfo} from './calendar-info';
|
||||
import {Subject} from 'rxjs';
|
||||
import {ConfigProvider} from '../config/config.provider';
|
||||
|
||||
const RECURRENCE_PATTERNS: Partial<
|
||||
Record<unitOfTime.Diff, string | undefined>
|
||||
> = {
|
||||
const RECURRENCE_PATTERNS: Partial<Record<unitOfTime.Diff, string | undefined>> = {
|
||||
year: 'yearly',
|
||||
month: 'monthly',
|
||||
week: 'weekly',
|
||||
@@ -40,12 +38,8 @@ export class CalendarService {
|
||||
calendarName = 'StApps';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
constructor(
|
||||
readonly calendar: Calendar,
|
||||
private readonly configProvider: ConfigProvider,
|
||||
) {
|
||||
this.calendarName =
|
||||
(this.configProvider.getValue('name') as string) ?? 'StApps';
|
||||
constructor(readonly calendar: Calendar, private readonly configProvider: ConfigProvider) {
|
||||
this.calendarName = (this.configProvider.getValue('name') as string) ?? 'StApps';
|
||||
}
|
||||
|
||||
async createCalendar(): Promise<CalendarInfo | undefined> {
|
||||
@@ -61,9 +55,7 @@ export class CalendarService {
|
||||
}
|
||||
|
||||
async findCalendar(name: string): Promise<CalendarInfo | undefined> {
|
||||
return (await this.listCalendars())?.find(
|
||||
(calendar: CalendarInfo) => calendar.name === name,
|
||||
);
|
||||
return (await this.listCalendars())?.find((calendar: CalendarInfo) => calendar.name === name);
|
||||
}
|
||||
|
||||
async purge(): Promise<CalendarInfo | undefined> {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {findRRules, RRule} from './ical';
|
||||
@@ -27,10 +27,7 @@ function expandRRule(rule: RRule): SCISO8601Date[] {
|
||||
|
||||
return shuffle(
|
||||
Array.from({
|
||||
length:
|
||||
Math.floor(
|
||||
moment(rule.until).diff(initial, rule.freq, true) / interval,
|
||||
) + 1,
|
||||
length: Math.floor(moment(rule.until).diff(initial, rule.freq, true) / interval) + 1,
|
||||
}).map((_, i) =>
|
||||
initial
|
||||
.clone()
|
||||
@@ -65,8 +62,7 @@ describe('iCal', () => {
|
||||
});
|
||||
|
||||
it('should find mixed recurrence patterns', () => {
|
||||
const singlePattern: SCISO8601Date =
|
||||
moment('2021-09-01T09:00').toISOString();
|
||||
const singlePattern: SCISO8601Date = moment('2021-09-01T09:00').toISOString();
|
||||
|
||||
const weeklyPattern: RRule = {
|
||||
freq: 'week',
|
||||
@@ -75,8 +71,9 @@ describe('iCal', () => {
|
||||
until: moment('2021-09-03T10:00').add(4, 'weeks').toISOString(),
|
||||
};
|
||||
|
||||
expect(
|
||||
findRRules(shuffle([singlePattern, ...expandRRule(weeklyPattern)])),
|
||||
).toEqual([singlePattern, weeklyPattern]);
|
||||
expect(findRRules(shuffle([singlePattern, ...expandRRule(weeklyPattern)]))).toEqual([
|
||||
singlePattern,
|
||||
weeklyPattern,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {
|
||||
SCDateSeries,
|
||||
@@ -85,27 +85,19 @@ export interface MergedRRule {
|
||||
/**
|
||||
* Merge compatible RRules to a single RRule with exceptions
|
||||
*/
|
||||
export function mergeRRules(
|
||||
rules: Array<RRule | SCISO8601Date>,
|
||||
allowExceptions = true,
|
||||
): MergedRRule[] {
|
||||
if (!allowExceptions)
|
||||
return rules.map(it => (typeof it === 'string' ? {date: it} : {rrule: it}));
|
||||
export function mergeRRules(rules: Array<RRule | SCISO8601Date>, allowExceptions = true): MergedRRule[] {
|
||||
if (!allowExceptions) return rules.map(it => (typeof it === 'string' ? {date: it} : {rrule: it}));
|
||||
/*map(groupBy(rules, it => `${it.freq}@${it.interval}`), it => {
|
||||
|
||||
});*/
|
||||
|
||||
return rules.map(it =>
|
||||
typeof it === 'string' ? {date: it} : {rrule: it},
|
||||
) /* TODO */;
|
||||
return rules.map(it => (typeof it === 'string' ? {date: it} : {rrule: it})) /* TODO */;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find RRules in a list of dates
|
||||
*/
|
||||
export function findRRules(
|
||||
dates: SCISO8601Date[],
|
||||
): Array<RRule | SCISO8601Date> {
|
||||
export function findRRules(dates: SCISO8601Date[]): Array<RRule | SCISO8601Date> {
|
||||
const sorted = dates.sort((a, b) => moment(a).unix() - moment(b).unix());
|
||||
|
||||
const output: Optional<RRule, 'freq'>[] = [
|
||||
@@ -121,9 +113,7 @@ export function findRRules(
|
||||
const next = sorted[i + 1] as SCISO8601Date | undefined;
|
||||
const element = output[output.length - 1];
|
||||
|
||||
const units: unitOfTime.Diff[] = element?.freq
|
||||
? [element.freq]
|
||||
: ['day', 'week', 'month', 'year'];
|
||||
const units: unitOfTime.Diff[] = element?.freq ? [element.freq] : ['day', 'week', 'month', 'year'];
|
||||
const freq = minBy(
|
||||
units.map(recurrence => ({
|
||||
recurrence: recurrence,
|
||||
@@ -179,8 +169,7 @@ function getICalData(
|
||||
uuid: dateSeries.uid,
|
||||
categories: [
|
||||
'stapps',
|
||||
...((translated.event() as SCThingWithCategories<string, never>)
|
||||
?.categories ?? []),
|
||||
...((translated.event() as SCThingWithCategories<string, never>)?.categories ?? []),
|
||||
],
|
||||
description: translated.event()?.description ?? translated.description(),
|
||||
geo: translated.inPlace()?.name,
|
||||
@@ -207,28 +196,23 @@ export function toICal(
|
||||
: dateSeries.dates,
|
||||
);
|
||||
|
||||
return mergeRRules(rrules, options.allowRRuleExceptions).map(
|
||||
(it, i, array) => ({
|
||||
...getICalData(dateSeries, translator),
|
||||
dates: dateSeries.dates,
|
||||
rrule: it.rrule,
|
||||
recurrenceSequence: array.length > 1 ? i + 1 : undefined,
|
||||
recurrenceSequenceAmount: array.length > 1 ? array.length : undefined,
|
||||
exceptionDates: it.exceptions,
|
||||
start: it.rrule?.from ?? it.date ?? dateSeries.dates[0],
|
||||
sequence: 0,
|
||||
duration: dateSeries.duration,
|
||||
}),
|
||||
);
|
||||
return mergeRRules(rrules, options.allowRRuleExceptions).map((it, i, array) => ({
|
||||
...getICalData(dateSeries, translator),
|
||||
dates: dateSeries.dates,
|
||||
rrule: it.rrule,
|
||||
recurrenceSequence: array.length > 1 ? i + 1 : undefined,
|
||||
recurrenceSequenceAmount: array.length > 1 ? array.length : undefined,
|
||||
exceptionDates: it.exceptions,
|
||||
start: it.rrule?.from ?? it.date ?? dateSeries.dates[0],
|
||||
sequence: 0,
|
||||
duration: dateSeries.duration,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export function toICalUpdates(
|
||||
dateSeries: SCDateSeries,
|
||||
translator: SCThingTranslator,
|
||||
): ICalEvent[] {
|
||||
export function toICalUpdates(dateSeries: SCDateSeries, translator: SCThingTranslator): ICalEvent[] {
|
||||
return (
|
||||
dateSeries.exceptions?.map(exception => ({
|
||||
...getICalData(dateSeries, translator),
|
||||
@@ -246,9 +230,7 @@ export function toICalUpdates(
|
||||
export function iso8601ToICalDateTime<T extends SCISO8601Date | undefined>(
|
||||
date: T,
|
||||
): T extends SCISO8601Date ? string : undefined {
|
||||
return (
|
||||
date ? `${moment(date).utc().format('YYYYMMDDTHHmmss')}Z` : undefined
|
||||
) as never;
|
||||
return (date ? `${moment(date).utc().format('YYYYMMDDTHHmmss')}Z` : undefined) as never;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -261,9 +243,7 @@ export function iso8601ToICalDate(date: SCISO8601Date): string {
|
||||
/**
|
||||
* Recursively stringify all linebreaks to \n strings
|
||||
*/
|
||||
function stringifyLinebreaks<T extends string | unknown[] | unknown>(
|
||||
value: T,
|
||||
): T {
|
||||
function stringifyLinebreaks<T extends string | unknown[] | unknown>(value: T): T {
|
||||
if (typeof value === 'string') {
|
||||
return value.replace(/\r?\n|\r/g, '\\n') as T;
|
||||
}
|
||||
@@ -307,9 +287,7 @@ export function serializeICalLike(iCal: ICalLike): string {
|
||||
/**
|
||||
* Removes all strings that are either undefined or end with 'undefined'
|
||||
*/
|
||||
function withoutNullishStrings<T extends string>(
|
||||
array: Array<T | `${string}${undefined}` | undefined>,
|
||||
): T[] {
|
||||
function withoutNullishStrings<T extends string>(array: Array<T | `${string}${undefined}` | undefined>): T[] {
|
||||
return array.filter(it => it && !it.endsWith('undefined')) as T[];
|
||||
}
|
||||
|
||||
@@ -318,9 +296,9 @@ function withoutNullishStrings<T extends string>(
|
||||
*/
|
||||
export function serializeRRule(rrule?: RRule): string | undefined {
|
||||
return rrule
|
||||
? `FREQ=${
|
||||
REPEAT_FREQUENCIES[rrule.freq ?? 's']
|
||||
};UNTIL=${iso8601ToICalDateTime(rrule.until)};INTERVAL=${rrule.interval}`
|
||||
? `FREQ=${REPEAT_FREQUENCIES[rrule.freq ?? 's']};UNTIL=${iso8601ToICalDateTime(rrule.until)};INTERVAL=${
|
||||
rrule.interval
|
||||
}`
|
||||
: undefined;
|
||||
}
|
||||
|
||||
@@ -402,8 +380,6 @@ export function getICalExport(
|
||||
excludeCancelledEvents: !includeCancelled,
|
||||
}),
|
||||
),
|
||||
...(includeCancelled
|
||||
? dateSeries.flatMap(event => toICalUpdates(event, translator))
|
||||
: []),
|
||||
...(includeCancelled ? dateSeries.flatMap(event => toICalUpdates(event, translator)) : []),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable unicorn/no-null */
|
||||
@@ -33,18 +33,11 @@ import {pick} from '../../_helpers/collections/pick';
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export function toDateSeriesRelevantData(
|
||||
dateSeries: SCDateSeries,
|
||||
): DateSeriesRelevantData {
|
||||
export function toDateSeriesRelevantData(dateSeries: SCDateSeries): DateSeriesRelevantData {
|
||||
return pick(dateSeries, dateSeriesRelevantKeys);
|
||||
}
|
||||
|
||||
export type DateSeriesRelevantKeys =
|
||||
| 'uid'
|
||||
| 'dates'
|
||||
| 'exceptions'
|
||||
| 'repeatFrequency'
|
||||
| 'duration';
|
||||
export type DateSeriesRelevantKeys = 'uid' | 'dates' | 'exceptions' | 'repeatFrequency' | 'duration';
|
||||
|
||||
export const dateSeriesRelevantKeys: Array<DateSeriesRelevantKeys> = [
|
||||
'uid',
|
||||
@@ -62,12 +55,9 @@ export const formatRelevantKeys: {
|
||||
) => string;
|
||||
} = {
|
||||
uid: value => value,
|
||||
dates: (value, dateFormatter) =>
|
||||
`[${value.map(it => dateFormatter.transform(it)).join(', ')}]`,
|
||||
exceptions: (value, dateFormatter) =>
|
||||
`[${value?.map(it => dateFormatter.transform(it)).join(', ') ?? ''}]`,
|
||||
repeatFrequency: (value, _, durationFormatter) =>
|
||||
durationFormatter.transform(value),
|
||||
dates: (value, dateFormatter) => `[${value.map(it => dateFormatter.transform(it)).join(', ')}]`,
|
||||
exceptions: (value, dateFormatter) => `[${value?.map(it => dateFormatter.transform(it)).join(', ') ?? ''}]`,
|
||||
repeatFrequency: (value, _, durationFormatter) => durationFormatter.transform(value),
|
||||
duration: (value, _, durationFormatter) => durationFormatter.transform(value),
|
||||
};
|
||||
|
||||
@@ -133,19 +123,12 @@ export class ScheduleProvider implements OnDestroy {
|
||||
|
||||
public get partialEvents$(): BehaviorSubject<DateSeriesRelevantData[]> {
|
||||
if (!this._partialEvents$) {
|
||||
const data = ScheduleProvider.get<DateSeriesRelevantData>(
|
||||
ScheduleProvider.partialEventsStorageKey,
|
||||
);
|
||||
const data = ScheduleProvider.get<DateSeriesRelevantData>(ScheduleProvider.partialEventsStorageKey);
|
||||
|
||||
this._partialEvents$ = new BehaviorSubject(data ?? []);
|
||||
this._partialEventsSubscription = this._partialEvents$.subscribe(
|
||||
result => {
|
||||
ScheduleProvider.set(
|
||||
ScheduleProvider.partialEventsStorageKey,
|
||||
result,
|
||||
);
|
||||
},
|
||||
);
|
||||
this._partialEventsSubscription = this._partialEvents$.subscribe(result => {
|
||||
ScheduleProvider.set(ScheduleProvider.partialEventsStorageKey, result);
|
||||
});
|
||||
}
|
||||
|
||||
return this._partialEvents$;
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<ion-header>
|
||||
<ion-toolbar color="primary" mode="ios">
|
||||
<ion-buttons slot="start">
|
||||
@@ -7,15 +22,8 @@
|
||||
<ion-title>{{ 'catalog.title' | translate | titlecase }}</ion-title>
|
||||
</ion-toolbar>
|
||||
<ion-toolbar color="primary" mode="md">
|
||||
<ion-segment
|
||||
(ionChange)="segmentChanged($event)"
|
||||
[value]="selectedSemesterUID"
|
||||
mode="md"
|
||||
>
|
||||
<ion-segment-button
|
||||
*ngFor="let semester of availableSemesters"
|
||||
[value]="semester.uid"
|
||||
>
|
||||
<ion-segment (ionChange)="segmentChanged($event)" [value]="selectedSemesterUID" mode="md">
|
||||
<ion-segment-button *ngFor="let semester of availableSemesters" [value]="semester.uid">
|
||||
<ion-label>{{ semester.acronym }}</ion-label>
|
||||
</ion-segment-button>
|
||||
</ion-segment>
|
||||
@@ -24,12 +32,7 @@
|
||||
|
||||
<ion-content>
|
||||
<ion-list *ngIf="catalogs && catalogs.length > 0">
|
||||
<ion-item
|
||||
*ngFor="let catalog of catalogs"
|
||||
button="true"
|
||||
lines="inset"
|
||||
(click)="notifySelect(catalog)"
|
||||
>
|
||||
<ion-item *ngFor="let catalog of catalogs" button="true" lines="inset" (click)="notifySelect(catalog)">
|
||||
<ion-label>
|
||||
<h2>{{ catalog.name }}</h2>
|
||||
<h3>{{ catalog.acronym }}</h3>
|
||||
@@ -37,8 +40,7 @@
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
<ion-list *ngIf="!catalogs">
|
||||
<stapps-skeleton-list-item *ngFor="let skeleton of [].constructor(10)">
|
||||
</stapps-skeleton-list-item>
|
||||
<stapps-skeleton-list-item *ngFor="let skeleton of [].constructor(10)"> </stapps-skeleton-list-item>
|
||||
</ion-list>
|
||||
<ion-grid *ngIf="catalogs && catalogs.length === 0">
|
||||
<ion-row>
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
/*!
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
ion-segment-button {
|
||||
max-width: 100%;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {Component, OnInit, OnDestroy} from '@angular/core';
|
||||
import {Router, ActivatedRoute} from '@angular/router';
|
||||
@@ -121,12 +121,8 @@ export class CatalogComponent implements OnInit, OnDestroy {
|
||||
this.activeSemester = this.availableSemesters[0];
|
||||
this.activeSemester =
|
||||
this.selectedSemesterUID === ''
|
||||
? this.availableSemesters.find(
|
||||
semester => semester.startDate <= today && semester.endDate > today,
|
||||
)
|
||||
: this.availableSemesters.find(
|
||||
semester => semester.uid === this.selectedSemesterUID,
|
||||
);
|
||||
? this.availableSemesters.find(semester => semester.startDate <= today && semester.endDate > today)
|
||||
: this.availableSemesters.find(semester => semester.uid === this.selectedSemesterUID);
|
||||
if (this.activeSemester && this.selectedSemesterUID === '') {
|
||||
this.selectedSemesterUID = this.activeSemester.uid;
|
||||
}
|
||||
@@ -134,10 +130,7 @@ export class CatalogComponent implements OnInit, OnDestroy {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
segmentChanged(event: any) {
|
||||
if (
|
||||
this.activeSemester &&
|
||||
this.activeSemester.uid !== (event.detail.value as string)
|
||||
) {
|
||||
if (this.activeSemester && this.activeSemester.uid !== (event.detail.value as string)) {
|
||||
this.updateLocation(event.detail.value as string);
|
||||
}
|
||||
|
||||
@@ -149,9 +142,7 @@ export class CatalogComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
updateLocation(semesterUID: string) {
|
||||
const url = this.router
|
||||
.createUrlTree(['/catalog/', semesterUID])
|
||||
.toString();
|
||||
const url = this.router.createUrlTree(['/catalog/', semesterUID]).toString();
|
||||
this.location.go(url);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 StApps
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -115,8 +115,6 @@ export class CatalogProvider {
|
||||
size: 10,
|
||||
});
|
||||
|
||||
return (response.data as SCSemester[]).sort((a, b) =>
|
||||
b.startDate.localeCompare(a.startDate, 'en'),
|
||||
);
|
||||
return (response.data as SCSemester[]).sort((a, b) => b.startDate.localeCompare(a.startDate, 'en'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 StApps
|
||||
* Copyright (C) 2023 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -31,15 +31,8 @@ describe('ConfigProvider', () => {
|
||||
let ngxLogger: jasmine.SpyObj<NGXLogger>;
|
||||
|
||||
beforeEach(() => {
|
||||
storageProviderSpy = jasmine.createSpyObj('StorageProvider', [
|
||||
'init',
|
||||
'get',
|
||||
'has',
|
||||
'put',
|
||||
]);
|
||||
const webHttpClientMethodSpy = jasmine.createSpyObj('StAppsWebHttpClient', [
|
||||
'request',
|
||||
]);
|
||||
storageProviderSpy = jasmine.createSpyObj('StorageProvider', ['init', 'get', 'has', 'put']);
|
||||
const webHttpClientMethodSpy = jasmine.createSpyObj('StAppsWebHttpClient', ['request']);
|
||||
ngxLogger = jasmine.createSpyObj('NGXLogger', ['log', 'error', 'warn']);
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
@@ -65,9 +58,7 @@ describe('ConfigProvider', () => {
|
||||
});
|
||||
|
||||
it('should fetch app configuration', async () => {
|
||||
spyOn(configProvider.client, 'handshake').and.returnValue(
|
||||
Promise.resolve(sampleIndexResponse),
|
||||
);
|
||||
spyOn(configProvider.client, 'handshake').and.returnValue(Promise.resolve(sampleIndexResponse));
|
||||
const result = await configProvider.fetch();
|
||||
expect(result).toEqual(sampleIndexResponse);
|
||||
});
|
||||
@@ -86,9 +77,7 @@ describe('ConfigProvider', () => {
|
||||
|
||||
it('should init from remote and saved config not available', async () => {
|
||||
storageProviderSpy.has.and.returnValue(Promise.resolve(false));
|
||||
spyOn(configProvider.client, 'handshake').and.returnValue(
|
||||
Promise.resolve(sampleIndexResponse),
|
||||
);
|
||||
spyOn(configProvider.client, 'handshake').and.returnValue(Promise.resolve(sampleIndexResponse));
|
||||
try {
|
||||
await configProvider.init();
|
||||
} catch (error) {
|
||||
@@ -97,9 +86,7 @@ describe('ConfigProvider', () => {
|
||||
expect(storageProviderSpy.has).toHaveBeenCalled();
|
||||
expect(storageProviderSpy.get).toHaveBeenCalledTimes(0);
|
||||
expect(configProvider.client.handshake).toHaveBeenCalled();
|
||||
expect(await configProvider.getValue('name')).toEqual(
|
||||
sampleIndexResponse.app.name,
|
||||
);
|
||||
expect(await configProvider.getValue('name')).toEqual(sampleIndexResponse.app.name);
|
||||
});
|
||||
|
||||
it('should throw error on failed initialisation', async () => {
|
||||
@@ -120,16 +107,11 @@ describe('ConfigProvider', () => {
|
||||
const wrongConfig = JSON.parse(JSON.stringify(sampleIndexResponse));
|
||||
wrongConfig.backend.SCVersion = '0.1.0';
|
||||
storageProviderSpy.get.and.returnValue(wrongConfig);
|
||||
spyOn(configProvider.client, 'handshake').and.returnValue(
|
||||
Promise.resolve(sampleIndexResponse),
|
||||
);
|
||||
spyOn(configProvider.client, 'handshake').and.returnValue(Promise.resolve(sampleIndexResponse));
|
||||
await configProvider.init();
|
||||
|
||||
expect(ngxLogger.warn).toHaveBeenCalledWith(
|
||||
new WrongConfigVersionInStorage(
|
||||
configProvider.scVersion,
|
||||
wrongConfig.backend.SCVersion,
|
||||
),
|
||||
new WrongConfigVersionInStorage(configProvider.scVersion, wrongConfig.backend.SCVersion),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -147,10 +129,7 @@ describe('ConfigProvider', () => {
|
||||
|
||||
it('should save app configuration', async () => {
|
||||
await configProvider.save(sampleIndexResponse);
|
||||
expect(storageProviderSpy.put).toHaveBeenCalledWith(
|
||||
STORAGE_KEY_CONFIG,
|
||||
sampleIndexResponse,
|
||||
);
|
||||
expect(storageProviderSpy.put).toHaveBeenCalledWith(STORAGE_KEY_CONFIG, sampleIndexResponse);
|
||||
});
|
||||
|
||||
it('should set app configuration', async () => {
|
||||
@@ -160,28 +139,18 @@ describe('ConfigProvider', () => {
|
||||
|
||||
it('should return app configuration value', async () => {
|
||||
storageProviderSpy.has.and.returnValue(Promise.resolve(true));
|
||||
storageProviderSpy.get.and.returnValue(
|
||||
Promise.resolve(sampleIndexResponse),
|
||||
);
|
||||
spyOn(configProvider.client, 'handshake').and.returnValue(
|
||||
Promise.resolve(sampleIndexResponse),
|
||||
);
|
||||
storageProviderSpy.get.and.returnValue(Promise.resolve(sampleIndexResponse));
|
||||
spyOn(configProvider.client, 'handshake').and.returnValue(Promise.resolve(sampleIndexResponse));
|
||||
await configProvider.init();
|
||||
expect(await configProvider.getValue('name')).toEqual(
|
||||
sampleIndexResponse.app.name,
|
||||
);
|
||||
expect(await configProvider.getValue('name')).toEqual(sampleIndexResponse.app.name);
|
||||
});
|
||||
|
||||
it('should init from storage when remote fails', async () => {
|
||||
storageProviderSpy.has.and.returnValue(Promise.resolve(true));
|
||||
storageProviderSpy.get.and.returnValue(
|
||||
Promise.resolve(sampleIndexResponse),
|
||||
);
|
||||
storageProviderSpy.get.and.returnValue(Promise.resolve(sampleIndexResponse));
|
||||
spyOn(configProvider.client, 'handshake').and.throwError('');
|
||||
await configProvider.init();
|
||||
|
||||
expect(configProvider.getValue('name')).toEqual(
|
||||
sampleIndexResponse.app.name,
|
||||
);
|
||||
expect(configProvider.getValue('name')).toEqual(sampleIndexResponse.app.name);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019, 2020 StApps
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -74,11 +74,7 @@ export class ConfigProvider {
|
||||
swHttpClient: StAppsWebHttpClient,
|
||||
private readonly logger: NGXLogger,
|
||||
) {
|
||||
this.client = new Client(
|
||||
swHttpClient,
|
||||
environment.backend_url,
|
||||
environment.backend_version,
|
||||
);
|
||||
this.client = new Client(swHttpClient, environment.backend_url, environment.backend_version);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,10 +127,7 @@ export class ConfigProvider {
|
||||
this.firstSession = false;
|
||||
this.logger.log(`initialised configuration from storage`);
|
||||
if (this.config.backend.SCVersion !== this.scVersion) {
|
||||
loadError = new WrongConfigVersionInStorage(
|
||||
this.scVersion,
|
||||
this.config.backend.SCVersion,
|
||||
);
|
||||
loadError = new WrongConfigVersionInStorage(this.scVersion, this.config.backend.SCVersion);
|
||||
}
|
||||
} catch (error) {
|
||||
loadError = error;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -38,10 +38,7 @@ export class ConfigInitError extends AppError {
|
||||
*/
|
||||
export class ConfigValueNotAvailable extends AppError {
|
||||
constructor(valueKey: string) {
|
||||
super(
|
||||
'ConfigValueNotAvailable',
|
||||
`No attribute "${valueKey}" in config available!`,
|
||||
);
|
||||
super('ConfigValueNotAvailable', `No attribute "${valueKey}" in config available!`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,11 +61,7 @@ export class DashboardCollapse {
|
||||
this.animationControl
|
||||
.create()
|
||||
.addElement(schedule)
|
||||
.fromTo(
|
||||
'transform',
|
||||
'translateY(0) scaleY(1)',
|
||||
'translateY(-75px) scaleY(0.8)',
|
||||
),
|
||||
.fromTo('transform', 'translateY(0) scaleY(1)', 'translateY(-75px) scaleY(0.8)'),
|
||||
this.animationControl
|
||||
.create()
|
||||
.addElement(schedule.querySelectorAll(':scope > a > *'))
|
||||
@@ -78,17 +74,12 @@ export class DashboardCollapse {
|
||||
}
|
||||
|
||||
private start() {
|
||||
this.collapseAnimation.progressStart(
|
||||
true,
|
||||
this.scrollContainer.scrollTop / 172,
|
||||
);
|
||||
this.collapseAnimation.progressStart(true, this.scrollContainer.scrollTop / 172);
|
||||
let pos = this.scrollContainer.scrollTop;
|
||||
const animate = () => {
|
||||
if (pos !== this.scrollContainer.scrollTop) {
|
||||
pos = this.scrollContainer.scrollTop;
|
||||
this.collapseAnimation.progressStep(
|
||||
this.scrollContainer.scrollTop / 172,
|
||||
);
|
||||
this.collapseAnimation.progressStep(this.scrollContainer.scrollTop / 172);
|
||||
}
|
||||
this.nextFrame = requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
@import '../../../theme/util/mixins';
|
||||
|
||||
@include ion-md-down {
|
||||
ion-toolbar, .logo, .schedule, .schedule > a > * {
|
||||
ion-toolbar,
|
||||
.logo,
|
||||
.schedule,
|
||||
.schedule > a > * {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
@@ -25,7 +28,6 @@
|
||||
}
|
||||
|
||||
.schedule {
|
||||
|
||||
> a {
|
||||
> * {
|
||||
transform-origin: center;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ Copyright (C) 2023 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
@@ -14,9 +14,7 @@
|
||||
-->
|
||||
<ion-header>
|
||||
<ion-toolbar #toolbar class="ion-hide-md-up">
|
||||
<ion-label slot="start">{{
|
||||
'dashboard.header.title' | daytimeKey | translate
|
||||
}}</ion-label>
|
||||
<ion-label slot="start">{{ 'dashboard.header.title' | daytimeKey | translate }}</ion-label>
|
||||
<div><ion-img src="assets/imgs/logo.png" class="logo"></ion-img></div>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
@@ -27,11 +25,7 @@
|
||||
</a>
|
||||
<!-- Avoid structural directives here, they might interfere with the collapse animation -->
|
||||
<a
|
||||
[routerLink]="
|
||||
nextEvent?.event
|
||||
? ['/data-detail', nextEvent!.event.uid]
|
||||
: ['/schedule/recurring']
|
||||
"
|
||||
[routerLink]="nextEvent?.event ? ['/data-detail', nextEvent!.event.uid] : ['/schedule/recurring']"
|
||||
class="schedule-item-button"
|
||||
>
|
||||
<ion-label>{{ 'dashboard.schedule.title' | translate }}</ion-label>
|
||||
@@ -43,18 +37,13 @@
|
||||
}}
|
||||
</ion-label>
|
||||
<ion-label>{{
|
||||
nextEvent?.event
|
||||
? nextEvent!.event.name
|
||||
: ('dashboard.schedule.noEventLink' | translate)
|
||||
nextEvent?.event ? nextEvent!.event.name : ('dashboard.schedule.noEventLink' | translate)
|
||||
}}</ion-label>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ion-content fullscreen="true" #ionContent>
|
||||
<stapps-search-section
|
||||
#search
|
||||
(focusin)="onSearchBarFocus($event)"
|
||||
></stapps-search-section>
|
||||
<stapps-search-section #search (focusin)="onSearchBarFocus($event)"></stapps-search-section>
|
||||
<stapps-news-section></stapps-news-section>
|
||||
<stapps-mensa-section></stapps-mensa-section>
|
||||
<stapps-favorites-section></stapps-favorites-section>
|
||||
|
||||
@@ -176,7 +176,6 @@ ion-content {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.swiper {
|
||||
background-color: var(--ion-color-primary-contrast);
|
||||
border-radius: var(--border-radius-default);
|
||||
@@ -195,7 +194,6 @@ ion-content {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ion-searchbar {
|
||||
padding: 0;
|
||||
--background: var(--ion-color-primary-contrast);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* Copyright (C) 2023 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -12,14 +12,7 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {
|
||||
Component,
|
||||
ElementRef,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import {Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
||||
import {Router} from '@angular/router';
|
||||
import {Location} from '@angular/common';
|
||||
import {Subscription} from 'rxjs';
|
||||
@@ -38,10 +31,7 @@ import {BreakpointObserver} from '@angular/cdk/layout';
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
templateUrl: './dashboard.component.html',
|
||||
styleUrls: [
|
||||
'./dashboard.component.scss',
|
||||
'/dashboard.collapse.component.scss',
|
||||
],
|
||||
styleUrls: ['./dashboard.component.scss', '/dashboard.collapse.component.scss'],
|
||||
})
|
||||
export class DashboardComponent implements OnInit, OnDestroy {
|
||||
/**
|
||||
@@ -103,12 +93,10 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this._eventUuidSubscription = this.scheduleProvider.uuids$.subscribe(
|
||||
async result => {
|
||||
this.eventUuids = result;
|
||||
await this.loadNextEvent();
|
||||
},
|
||||
);
|
||||
this._eventUuidSubscription = this.scheduleProvider.uuids$.subscribe(async result => {
|
||||
this.eventUuids = result;
|
||||
await this.loadNextEvent();
|
||||
});
|
||||
await SplashScreen.hide();
|
||||
|
||||
this.collapseAnimation = new DashboardCollapse(
|
||||
@@ -120,12 +108,10 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
|
||||
this.subscriptions.push(
|
||||
this.breakpointObserver
|
||||
.observe(['(min-width: 768px)'])
|
||||
.subscribe(async state => {
|
||||
await this.collapseAnimation.ready;
|
||||
this.collapseAnimation.active = !state.matches;
|
||||
}),
|
||||
this.breakpointObserver.observe(['(min-width: 768px)']).subscribe(async state => {
|
||||
await this.collapseAnimation.ready;
|
||||
this.collapseAnimation.active = !state.matches;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -141,8 +127,7 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
||||
time: new Date(
|
||||
series.dates
|
||||
.sort((a, b) => new Date(a).getTime() - new Date(b).getTime())
|
||||
.find(date => new Date(date) > new Date()) ||
|
||||
Number.POSITIVE_INFINITY,
|
||||
.find(date => new Date(date) > new Date()) || Number.POSITIVE_INFINITY,
|
||||
).getTime(),
|
||||
series,
|
||||
}))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 StApps
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -37,11 +37,7 @@ export class DashboardProvider {
|
||||
* @param from From which (results) page to start
|
||||
* @param filters Additional filters to apply
|
||||
*/
|
||||
async getNews(
|
||||
size: number,
|
||||
from: number,
|
||||
filters?: SCSearchFilter[],
|
||||
): Promise<SCMessage[]> {
|
||||
async getNews(size: number, from: number, filters?: SCSearchFilter[]): Promise<SCMessage[]> {
|
||||
const query: SCSearchQuery = {
|
||||
filter: {
|
||||
type: 'boolean',
|
||||
@@ -73,10 +69,7 @@ export class DashboardProvider {
|
||||
|
||||
if (typeof filters !== 'undefined') {
|
||||
for (const filter of filters) {
|
||||
(
|
||||
(query.filter as SCSearchBooleanFilter)
|
||||
.arguments as SCBooleanFilterArguments
|
||||
).filters.push(filter);
|
||||
((query.filter as SCSearchBooleanFilter).arguments as SCBooleanFilterArguments).filters.push(filter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,22 +35,13 @@
|
||||
<ion-item *ngFor="let item of items">
|
||||
<ion-reorder slot="start"></ion-reorder>
|
||||
<ion-label>{{ item.labelLocalized }}</ion-label>
|
||||
<ion-toggle
|
||||
slot="end"
|
||||
[checked]="item.active"
|
||||
[(ngModel)]="item.active"
|
||||
></ion-toggle>
|
||||
<ion-toggle slot="end" [checked]="item.active" [(ngModel)]="item.active"></ion-toggle>
|
||||
</ion-item>
|
||||
</ion-reorder-group>
|
||||
|
||||
<ion-radio-group
|
||||
*ngSwitchCase="type === types.RADIOBOXES"
|
||||
[(ngModel)]="selectedValue"
|
||||
>
|
||||
<ion-radio-group *ngSwitchCase="type === types.RADIOBOXES" [(ngModel)]="selectedValue">
|
||||
<ion-list-header>
|
||||
<ion-label>{{
|
||||
'dashboard.canteens.choose_favorite' | translate
|
||||
}}</ion-label>
|
||||
<ion-label>{{ 'dashboard.canteens.choose_favorite' | translate }}</ion-label>
|
||||
</ion-list-header>
|
||||
<ion-item *ngFor="let item of items">
|
||||
<ion-label>{{ item.labelLocalized }}</ion-label>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<ion-label class="section-headline"
|
||||
@@ -22,11 +22,6 @@
|
||||
(click)="onEditClick()"
|
||||
name="edit_square"
|
||||
></ion-icon>
|
||||
<ion-icon
|
||||
size="25"
|
||||
*ngIf="customIcon"
|
||||
(click)="onEditClick()"
|
||||
[name]="customIcon"
|
||||
></ion-icon>
|
||||
<ion-icon size="25" *ngIf="customIcon" (click)="onEditClick()" [name]="customIcon"></ion-icon>
|
||||
</ion-label>
|
||||
<ng-content></ng-content>
|
||||
|
||||
@@ -12,14 +12,7 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
HostBinding,
|
||||
Input,
|
||||
OnInit,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
import {Component, EventEmitter, HostBinding, Input, OnInit, Output} from '@angular/core';
|
||||
|
||||
/**
|
||||
* Shows a horizontal list of action chips
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<stapps-section
|
||||
[title]="'dashboard.favorites.title' | translate"
|
||||
[isEditable]="true"
|
||||
(onEdit)="onSectionEdit()"
|
||||
>
|
||||
<div *ngIf="(items | async)?.length" class="container">
|
||||
<div
|
||||
*ngFor="let item of items | async"
|
||||
class="card clickable"
|
||||
(click)="notifySelect(item)"
|
||||
>
|
||||
<div *ngFor="let item of items | async" class="card clickable" (click)="notifySelect(item)">
|
||||
<ion-thumbnail class="ion-margin-end">
|
||||
<ion-icon color="dark" [attr.name]="item.type | dataIcon"></ion-icon>
|
||||
</ion-thumbnail>
|
||||
@@ -21,9 +32,7 @@
|
||||
<div class="card">
|
||||
<ion-label>
|
||||
{{ 'dashboard.favorites.no_favorite_prefix' | translate }}
|
||||
<a (click)="onSectionEdit()">{{
|
||||
'dashboard.favorites.no_favorite_link' | translate
|
||||
}}</a>
|
||||
<a (click)="onSectionEdit()">{{ 'dashboard.favorites.no_favorite_link' | translate }}</a>
|
||||
{{ 'dashboard.favorites.no_favorite_suffix' | translate }}
|
||||
</ion-label>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*!
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
||||
@@ -10,7 +25,7 @@
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
min-height: var(--size);
|
||||
margin-bottom: var(--spacing-sm);
|
||||
margin-bottom: var(--spacing-sm);
|
||||
|
||||
ion-thumbnail {
|
||||
position: absolute;
|
||||
|
||||
@@ -16,12 +16,7 @@ import {Component, OnInit} from '@angular/core';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {AlertController, AnimationController} from '@ionic/angular';
|
||||
import {combineLatest} from 'rxjs';
|
||||
import {
|
||||
debounceTime,
|
||||
distinctUntilChanged,
|
||||
startWith,
|
||||
take,
|
||||
} from 'rxjs/operators';
|
||||
import {debounceTime, distinctUntilChanged, startWith, take} from 'rxjs/operators';
|
||||
import {NGXLogger} from 'ngx-logger';
|
||||
import {SCThings} from '@openstapps/core';
|
||||
|
||||
@@ -42,10 +37,7 @@ import {ConfigProvider} from '../../../config/config.provider';
|
||||
templateUrl: 'favorites-section.component.html',
|
||||
styleUrls: ['favorites-section.component.scss'],
|
||||
})
|
||||
export class FavoritesSectionComponent
|
||||
extends SearchPageComponent
|
||||
implements OnInit
|
||||
{
|
||||
export class FavoritesSectionComponent extends SearchPageComponent implements OnInit {
|
||||
constructor(
|
||||
protected readonly alertController: AlertController,
|
||||
protected dataProvider: DataProvider,
|
||||
@@ -100,9 +92,7 @@ export class FavoritesSectionComponent
|
||||
.pipe(take(1))
|
||||
.subscribe(result => {
|
||||
this.items = new Promise(resolve => {
|
||||
resolve(
|
||||
result.data && result.data.filter(item => !this.isMensaThing(item)),
|
||||
);
|
||||
resolve(result.data && result.data.filter(item => !this.isMensaThing(item)));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,13 +12,7 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
OnInit,
|
||||
OnChanges,
|
||||
SimpleChanges,
|
||||
} from '@angular/core';
|
||||
import {Component, Input, OnInit, OnChanges, SimpleChanges} from '@angular/core';
|
||||
import {SCDish, SCPlace, SCThings} from '@openstapps/core';
|
||||
import {PlaceMensaService} from '../../../data/types/place/special/mensa/place-mensa-service';
|
||||
|
||||
|
||||
@@ -1,20 +1,31 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<stapps-section
|
||||
[title]="'dashboard.canteens.title' | translate"
|
||||
[isEditable]="true"
|
||||
(onEdit)="onSectionEdit()"
|
||||
>
|
||||
<ng-container *ngIf="(items | async)?.length">
|
||||
<stapps-mensa-section-content
|
||||
[items]="items | async"
|
||||
></stapps-mensa-section-content>
|
||||
<stapps-mensa-section-content [items]="items | async"></stapps-mensa-section-content>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!(items | async)?.length">
|
||||
<div class="card">
|
||||
<ion-label>
|
||||
{{ 'dashboard.canteens.no_favorite_prefix' | translate }}
|
||||
<a (click)="onSectionEdit()">{{
|
||||
'dashboard.canteens.no_favorite_link' | translate
|
||||
}}</a>
|
||||
<a (click)="onSectionEdit()">{{ 'dashboard.canteens.no_favorite_link' | translate }}</a>
|
||||
{{ 'dashboard.canteens.no_favorite_suffix' | translate }}
|
||||
</ion-label>
|
||||
</div>
|
||||
|
||||
@@ -14,18 +14,9 @@
|
||||
*/
|
||||
import {Component} from '@angular/core';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {
|
||||
AlertController,
|
||||
AnimationController,
|
||||
ModalController,
|
||||
} from '@ionic/angular';
|
||||
import {AlertController, AnimationController, ModalController} from '@ionic/angular';
|
||||
import {combineLatest, Subscription} from 'rxjs';
|
||||
import {
|
||||
debounceTime,
|
||||
distinctUntilChanged,
|
||||
startWith,
|
||||
take,
|
||||
} from 'rxjs/operators';
|
||||
import {debounceTime, distinctUntilChanged, startWith, take} from 'rxjs/operators';
|
||||
import {NGXLogger} from 'ngx-logger';
|
||||
import {SCThings} from '@openstapps/core';
|
||||
|
||||
@@ -93,11 +84,7 @@ export class MensaSectionComponent extends FoodDataListComponent {
|
||||
]).subscribe(async query => {
|
||||
this.queryText = query[0];
|
||||
this.from = 0;
|
||||
if (
|
||||
typeof this.filterQuery !== 'undefined' ||
|
||||
this.queryText?.length > 0 ||
|
||||
this.showDefaultData
|
||||
) {
|
||||
if (typeof this.filterQuery !== 'undefined' || this.queryText?.length > 0 || this.showDefaultData) {
|
||||
await this.fetchAndUpdateItems();
|
||||
this.queryChanged.next();
|
||||
}
|
||||
@@ -114,9 +101,7 @@ export class MensaSectionComponent extends FoodDataListComponent {
|
||||
.pipe(take(1))
|
||||
.subscribe(result => {
|
||||
this.items = new Promise(resolve => {
|
||||
resolve(
|
||||
result.data && result.data.filter(item => this.isMensaThing(item)),
|
||||
);
|
||||
resolve(result.data && result.data.filter(item => this.isMensaThing(item)));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -19,11 +19,7 @@
|
||||
[isSectionExtended]="true"
|
||||
(onEdit)="onSectionEdit()"
|
||||
>
|
||||
<swiper
|
||||
[config]="sliderOptions"
|
||||
slidesPerView="auto"
|
||||
class="navigation-swiper card-swiper"
|
||||
>
|
||||
<swiper [config]="sliderOptions" slidesPerView="auto" class="navigation-swiper card-swiper">
|
||||
<ng-template swiperSlide *ngFor="let item of activeMenuItems">
|
||||
<a [routerLink]="menuItems[item].link" class="card">
|
||||
<ion-icon size="40" [name]="menuItems[item].icon"></ion-icon>
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
/*!
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.navigation-swiper.swiper {
|
||||
|
||||
.swiper-slide {
|
||||
|
||||
a {
|
||||
font-family: var(--ion-font-family);
|
||||
font-size: var(--font-size-xs);
|
||||
|
||||
@@ -17,10 +17,7 @@ import {ModalController} from '@ionic/angular';
|
||||
import {EditModalComponent} from '../../edit-modal/edit-modal.component';
|
||||
import {DEFAULT_ACTIVE_MENU_ITEMS, MENU_ITEMS} from './menu-items.config';
|
||||
import {MenuItemKey} from './menu-item.interface';
|
||||
import {
|
||||
EditModalItem,
|
||||
EditModalTypeEnum,
|
||||
} from '../../edit-modal/edit-modal-type.enum';
|
||||
import {EditModalItem, EditModalTypeEnum} from '../../edit-modal/edit-modal-type.enum';
|
||||
import {StorageProvider} from '../../../storage/storage.provider';
|
||||
import {TranslatePipe} from '@ngx-translate/core';
|
||||
|
||||
@@ -67,15 +64,10 @@ export class NavigationSectionComponent implements OnInit {
|
||||
*/
|
||||
async getItems() {
|
||||
if (await this.storageProvider.has(DASHBOARD_NAVIGATION)) {
|
||||
const storedMenuItems: string = await this.storageProvider.get(
|
||||
DASHBOARD_NAVIGATION,
|
||||
);
|
||||
const storedMenuItems: string = await this.storageProvider.get(DASHBOARD_NAVIGATION);
|
||||
if (storedMenuItems) {
|
||||
const parsedMenuItems = JSON.parse(storedMenuItems);
|
||||
if (
|
||||
Array.isArray(parsedMenuItems) &&
|
||||
parsedMenuItems.every(it => typeof it === 'string')
|
||||
) {
|
||||
if (Array.isArray(parsedMenuItems) && parsedMenuItems.every(it => typeof it === 'string')) {
|
||||
this.activeMenuItems = parsedMenuItems;
|
||||
}
|
||||
}
|
||||
@@ -87,10 +79,7 @@ export class NavigationSectionComponent implements OnInit {
|
||||
*/
|
||||
updateActiveItems(items: MenuItemKey[]) {
|
||||
this.activeMenuItems = items;
|
||||
void this.storageProvider.put<string>(
|
||||
DASHBOARD_NAVIGATION,
|
||||
JSON.stringify(this.activeMenuItems),
|
||||
);
|
||||
void this.storageProvider.put<string>(DASHBOARD_NAVIGATION, JSON.stringify(this.activeMenuItems));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,9 +103,7 @@ export class NavigationSectionComponent implements OnInit {
|
||||
modal.onDidDismiss().then(result => {
|
||||
if (result.data?.items) {
|
||||
this.updateActiveItems(
|
||||
result.data.items
|
||||
.filter((it: EditModalItem) => it.active)
|
||||
.map((it: EditModalItem) => it.id),
|
||||
result.data.items.filter((it: EditModalItem) => it.active).map((it: EditModalItem) => it.id),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<stapps-section
|
||||
@@ -35,9 +35,7 @@
|
||||
</ng-template>
|
||||
<ng-template swiperSlide>
|
||||
<a [routerLink]="['/news']" class="card more-news">
|
||||
<ion-label>{{
|
||||
'dashboard.news.moreNews' | translate | titlecase
|
||||
}}</ion-label>
|
||||
<ion-label>{{ 'dashboard.news.moreNews' | translate | titlecase }}</ion-label>
|
||||
<ion-thumbnail class="ion-margin-end">
|
||||
<ion-icon color="dark" name="read_more" size="128"></ion-icon>
|
||||
</ion-thumbnail>
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
.news-swiper.swiper {
|
||||
/*!
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.news-swiper.swiper {
|
||||
.swiper-slide {
|
||||
padding: 0;
|
||||
|
||||
|
||||
@@ -17,10 +17,7 @@ import {Router} from '@angular/router';
|
||||
import {NewsPageComponent} from '../../../news/page/news-page.component';
|
||||
import {NewsProvider} from '../../../news/news.provider';
|
||||
import {SettingsProvider} from '../../../settings/settings.provider';
|
||||
import {
|
||||
newsFilterSettingsFieldsMapping,
|
||||
NewsFilterSettingsNames,
|
||||
} from '../../../news/news-filter-settings';
|
||||
import {newsFilterSettingsFieldsMapping, NewsFilterSettingsNames} from '../../../news/news-filter-settings';
|
||||
import {DataProvider} from '../../../data/data.provider';
|
||||
import {SCSearchValueFilter} from '@openstapps/core';
|
||||
|
||||
@@ -58,11 +55,7 @@ export class NewsSectionComponent extends NewsPageComponent implements OnInit {
|
||||
*/
|
||||
filtersMap = new Map<NewsFilterSettingsNames, SCSearchValueFilter>();
|
||||
|
||||
constructor(
|
||||
newsProvider: NewsProvider,
|
||||
settingsProvider: SettingsProvider,
|
||||
private router: Router,
|
||||
) {
|
||||
constructor(newsProvider: NewsProvider, settingsProvider: SettingsProvider, private router: Router) {
|
||||
super(newsProvider, settingsProvider);
|
||||
}
|
||||
|
||||
@@ -72,9 +65,7 @@ export class NewsSectionComponent extends NewsPageComponent implements OnInit {
|
||||
this.filtersMap.set(
|
||||
setting.name as NewsFilterSettingsNames,
|
||||
DataProvider.createValueFilter(
|
||||
newsFilterSettingsFieldsMapping[
|
||||
setting.name as NewsFilterSettingsNames
|
||||
],
|
||||
newsFilterSettingsFieldsMapping[setting.name as NewsFilterSettingsNames],
|
||||
setting.value as string,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<stapps-section
|
||||
title="{{ 'dashboard.navigation.item.search' | translate }}"
|
||||
[isEditable]="false"
|
||||
>
|
||||
<stapps-section title="{{ 'dashboard.navigation.item.search' | translate }}" [isEditable]="false">
|
||||
<div class="searchbar">
|
||||
<ion-input
|
||||
type="search"
|
||||
@@ -27,12 +24,6 @@
|
||||
(search)="onSubmitSearch()"
|
||||
[(ngModel)]="searchTerm"
|
||||
></ion-input>
|
||||
<ion-icon
|
||||
size="35"
|
||||
weight="300"
|
||||
name="search"
|
||||
(click)="onSubmitSearch()"
|
||||
class="clickable"
|
||||
></ion-icon>
|
||||
<ion-icon size="35" weight="300" name="search" (click)="onSubmitSearch()" class="clickable"></ion-icon>
|
||||
</div>
|
||||
</stapps-section>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 StApps
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -41,8 +41,7 @@ export class ActionChipListComponent {
|
||||
locate: false, // TODO: reimplement this at a later date
|
||||
event:
|
||||
item.type === SCThingType.AcademicEvent ||
|
||||
(item.type === SCThingType.DateSeries &&
|
||||
(item as SCDateSeries).dates.length > 0),
|
||||
(item.type === SCThingType.DateSeries && (item as SCDateSeries).dates.length > 0),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<div>
|
||||
<stapps-locate-action-chip
|
||||
*ngIf="applicable.locate"
|
||||
[item]="item"
|
||||
></stapps-locate-action-chip>
|
||||
<stapps-locate-action-chip *ngIf="applicable.locate" [item]="item"></stapps-locate-action-chip>
|
||||
<!-- Add Event Chip needs to load data and should be the last -->
|
||||
<stapps-add-event-action-chip
|
||||
*ngIf="applicable.event"
|
||||
[item]="item"
|
||||
></stapps-add-event-action-chip>
|
||||
<stapps-add-event-action-chip *ngIf="applicable.event" [item]="item"></stapps-add-event-action-chip>
|
||||
</div>
|
||||
|
||||
@@ -24,10 +24,7 @@ import {
|
||||
chipSkeletonTransition,
|
||||
chipTransition,
|
||||
} from '../../../../animation/skeleton-transitions/chip-loading-transition';
|
||||
import {
|
||||
AddEventStates,
|
||||
AddEventStatesMap,
|
||||
} from './add-event-action-chip.config';
|
||||
import {AddEventStates, AddEventStatesMap} from './add-event-action-chip.config';
|
||||
import {EditEventSelectionComponent} from '../edit-event-selection.component';
|
||||
import {AddEventReviewModalComponent} from '../../../calendar/add-event-review-modal.component';
|
||||
|
||||
@@ -183,31 +180,25 @@ export class AddEventActionChipComponent implements OnDestroy {
|
||||
})
|
||||
.then(it => it.data as SCDateSeries[]);
|
||||
|
||||
this.uuidSubscription = this.scheduleProvider.uuids$.subscribe(
|
||||
async result => {
|
||||
this.uuids = result;
|
||||
const associatedDateSeries = await this.associatedDateSeries;
|
||||
if (associatedDateSeries.length === 0) {
|
||||
this.applyState(AddEventStates.UNAVAILABLE);
|
||||
this.uuidSubscription = this.scheduleProvider.uuids$.subscribe(async result => {
|
||||
this.uuids = result;
|
||||
const associatedDateSeries = await this.associatedDateSeries;
|
||||
if (associatedDateSeries.length === 0) {
|
||||
this.applyState(AddEventStates.UNAVAILABLE);
|
||||
|
||||
return;
|
||||
}
|
||||
switch (
|
||||
associatedDateSeries
|
||||
.map(it => it.uid)
|
||||
.filter(it => !this.uuids.includes(it)).length
|
||||
) {
|
||||
case 0:
|
||||
this.applyState(AddEventStates.ADDED_ALL);
|
||||
break;
|
||||
case associatedDateSeries.length:
|
||||
this.applyState(AddEventStates.REMOVED_ALL);
|
||||
break;
|
||||
default:
|
||||
this.applyState(AddEventStates.ADDED_SOME);
|
||||
break;
|
||||
}
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
switch (associatedDateSeries.map(it => it.uid).filter(it => !this.uuids.includes(it)).length) {
|
||||
case 0:
|
||||
this.applyState(AddEventStates.ADDED_ALL);
|
||||
break;
|
||||
case associatedDateSeries.length:
|
||||
this.applyState(AddEventStates.REMOVED_ALL);
|
||||
break;
|
||||
default:
|
||||
this.applyState(AddEventStates.ADDED_SOME);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,18 +41,9 @@
|
||||
slot="end"
|
||||
fill="clear"
|
||||
(click)="export()"
|
||||
[disabled]="
|
||||
!(
|
||||
selection.selection.indeterminate ||
|
||||
selection.selection.checked
|
||||
)
|
||||
"
|
||||
[disabled]="!(selection.selection.indeterminate || selection.selection.checked)"
|
||||
>
|
||||
{{
|
||||
'schedule.toCalendar.reviewModal.DOWNLOAD'
|
||||
| translate
|
||||
| titlecase
|
||||
}}
|
||||
{{ 'schedule.toCalendar.reviewModal.DOWNLOAD' | translate | titlecase }}
|
||||
<ion-icon slot="end" name="download"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-toolbar>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@import "src/theme/common/ion-content-parallax";
|
||||
@import 'src/theme/common/ion-content-parallax';
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
@@ -38,7 +38,7 @@
|
||||
--background: var(--ion-color-primary);
|
||||
--color: var(--ion-color-primary-contrast);
|
||||
|
||||
@include ion-content-parallax($content-size: 160px)
|
||||
@include ion-content-parallax($content-size: 160px);
|
||||
}
|
||||
|
||||
ion-footer > ion-toolbar {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user