feat: separate prettier from eslint

This commit is contained in:
Rainer Killinger
2023-01-11 13:25:18 +01:00
committed by Thea Schöbl
parent 939fb6ef0f
commit a88d000ccd
381 changed files with 17952 additions and 38411 deletions

View File

@@ -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]]);
});
});

View File

@@ -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)));

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>;

View File

@@ -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);

View File

@@ -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];

View File

@@ -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'},
]);
});
});

View File

@@ -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) || '';

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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]);
});

View File

@@ -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;

View File

@@ -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

View File

@@ -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'],
},
{

View File

@@ -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);

View File

@@ -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"

View File

@@ -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)];
}
/**

View File

@@ -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')]),
),
]);

View File

@@ -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},

View File

@@ -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;

View File

@@ -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: [

View File

@@ -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">

View File

@@ -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>

View File

@@ -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] ?? {};
}
}

View File

@@ -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

View File

@@ -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()

View File

@@ -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);

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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/>.
*/

View File

@@ -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) {

View File

@@ -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>

View File

@@ -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 }},

View File

@@ -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;

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 = {};

View File

@@ -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: {

View File

@@ -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);

View File

@@ -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}`;
}

View File

@@ -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;

View File

@@ -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),
]),
);
}

View File

@@ -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']);

View File

@@ -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);
}

View File

@@ -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();
};

View File

@@ -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);
};

View File

@@ -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();
};

View File

@@ -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);
}
}

View File

@@ -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 = {

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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}`,
},
};

View File

@@ -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)));
});
}
}

View File

@@ -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,
);
}

View File

@@ -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()) {

View File

@@ -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'),
});
}
}

View File

@@ -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>

View File

@@ -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> {

View File

@@ -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,
]);
});
});

View File

@@ -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)) : []),
];
}

View File

@@ -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$;

View File

@@ -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>

View File

@@ -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%;

View File

@@ -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);
}

View File

@@ -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'));
}
}

View File

@@ -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);
});
});

View File

@@ -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;

View File

@@ -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!`);
}
}

View File

@@ -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);
};

View File

@@ -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;

View File

@@ -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>

View File

@@ -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);

View File

@@ -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,
}))

View File

@@ -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);
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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;

View File

@@ -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)));
});
});
}

View File

@@ -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';

View File

@@ -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>

View File

@@ -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)));
});
});
}

View File

@@ -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>

View File

@@ -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);

View File

@@ -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),
);
}
});

View File

@@ -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>

View File

@@ -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;

View File

@@ -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,
),
);

View File

@@ -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>

View File

@@ -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),
};
}

View File

@@ -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>

View File

@@ -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;
}
});
}
}

View File

@@ -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>

View File

@@ -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