Compare commits

...

35 Commits

Author SHA1 Message Date
Thea Schöbl
580ebee362 fix: user gets logged out when their token expires
Closes #230
2024-11-28 18:04:08 +00:00
Rainer Killinger
07e5c80223 docs: update changelogs for release
ci: publish release
2024-11-05 11:40:19 +01:00
Jovan Krunić
2ac840d845 fix: observable immediately returns false if scheduled beyond max delay
Closes #229
2024-11-04 18:04:48 +01:00
Thea Schöbl
8b581ef9ca feat: rework about pages changelog 2024-11-04 07:50:47 +00:00
Rainer Killinger
e2d5d4f187 docs: update changelogs for release
ci: publish release
2024-10-18 16:52:57 +02:00
Rainer Killinger
496b50d892 fix: update jsonpath-plus depenency 2024-10-18 16:12:29 +02:00
e6c17c860b fix: id cards are wiped/replaced when an error happens in the pipe 2024-10-11 15:33:03 +02:00
Jovan Krunić
bb1f596bfc fix: enable starting the app without backend
Closes #223
2024-09-30 11:03:53 +00:00
0c49fd8c34 refactor: update nix dependencies 2024-09-30 13:00:59 +02:00
Thea Schöbl
ce5016a992 fix: text of the feedback form not fully visible
Closes #227
2024-09-18 09:53:50 +00:00
3ac8c04765 fix: date range is now pipe has issues around boundaries 2024-08-06 17:43:59 +02:00
Rainer Killinger
5c260dd26b refactor: update android build dependencies for API level 34 2024-08-05 12:51:47 +00:00
Rainer Killinger
f2e1a6ddd5 docs: update changelogs for release
ci: publish release
2024-08-02 10:19:44 +02:00
Rainer Killinger
ccacfbf710 fix: publish-packages script 2024-08-02 10:18:57 +02:00
Rainer Killinger
b249b329f7 refactor: use development backend for non production environment 2024-08-01 17:44:21 +02:00
Jovan Krunić
7afc24f1bc fix: use major version config from storage when offline
Closes #218
2024-08-01 17:44:20 +02:00
Rainer Killinger
7276525dfa fix: app-builder image build due to nodejs versions <18.19 not being supported by angular 17 2024-08-01 15:24:59 +02:00
Jovan Krunić
c9e881582f refactor: set production version of the backend 2024-07-19 15:20:06 +02:00
Jovan Krunić
df681de083 feat: show title if available on library account profile
Closes #215
2024-07-19 15:20:06 +02:00
802a7a3fa7 fix: docs generation 2024-07-19 15:20:06 +02:00
Jovan Krunić
343d03e647 feat: library account adjustments
Closes #214
2024-07-19 15:20:06 +02:00
727b92911e fix: list item layout broken 2024-07-19 15:20:06 +02:00
11bc987807 fix: elasticsearch integration spams errors 2024-07-19 15:20:06 +02:00
Rainer Killinger
b8faae5988 docs: update changelogs for release
ci: publish release
2024-06-28 17:39:39 +02:00
67ab1fd613 fix: geo.point has wrong mapping 2024-06-28 17:25:32 +02:00
Rainer Killinger
142079bf0e refactor: set app backend to dev version 2024-06-28 12:02:59 +02:00
Rainer Killinger
5050ac90eb docs: update changelogs for release
ci: publish release
2024-06-28 09:28:39 +02:00
Rainer Killinger
688bc5f2e7 refactor: add changeset 2024-06-28 09:03:54 +02:00
Rainer Killinger
ad174dd7d7 refactor: use static map files from backend 2024-06-28 08:59:08 +02:00
Rainer Killinger
26e654f5b8 fix: app license overview 2024-06-27 14:57:24 +02:00
Rainer Killinger
5439484a90 fix: changelog typo 2024-06-27 12:06:52 +02:00
Rainer Killinger
68f3366a27 refactor: adjustments for recent PAIA changes 2024-06-27 09:23:55 +00:00
Jovan Krunić
dea9a82105 fix: do not fetch remote configuration if offline
Closes #206
2024-06-17 09:55:29 +00:00
39d2801114 feat: store id cards 2024-06-12 13:51:46 +02:00
Rainer Killinger
341b209092 refactor: display id-cards in their own modal 2024-06-05 11:07:30 +00:00
108 changed files with 5484 additions and 3253 deletions

View File

@@ -1,5 +1,31 @@
# @openstapps/backend # @openstapps/backend
## 3.3.2
### Patch Changes
- @openstapps/core@3.3.2
- @openstapps/core-tools@3.3.2
## 3.3.1
### Patch Changes
- 67ab1fd6: fix for geo.point mapping
## 3.3.0
### Minor Changes
- 688bc5f2: v3.3.0 changes
### Patch Changes
- Updated dependencies [688bc5f2]
- @openstapps/core@3.3.0
- @openstapps/core-tools@3.3.0
- @openstapps/logger@3.0.0
## 3.2.0 ## 3.2.0
### Minor Changes ### Minor Changes

View File

@@ -2,7 +2,7 @@
The Goethe-Uni App got even better! The Goethe-Uni App got even better!
## Completelty new map view ## Completely new map view
We overhauled the map to offer you a clearer and faster and overview. We overhauled the map to offer you a clearer and faster and overview.

View File

@@ -1,7 +1,7 @@
{ {
"name": "@openstapps/backend", "name": "@openstapps/backend",
"description": "A reference implementation for a StApps backend", "description": "A reference implementation for a StApps backend",
"version": "3.2.0", "version": "3.3.2",
"private": true, "private": true,
"type": "module", "type": "module",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",

View File

@@ -25,7 +25,7 @@ export function buildDistanceFilter(
): QueryDslSpecificQueryContainer<'geo_distance'> { ): QueryDslSpecificQueryContainer<'geo_distance'> {
const geoObject: QueryDslGeoDistanceQuery = { const geoObject: QueryDslGeoDistanceQuery = {
distance: `${filter.arguments.distance}m`, distance: `${filter.arguments.distance}m`,
[`${filter.arguments.field}.point.coordinates`]: { [`${filter.arguments.field}.point`]: {
lat: filter.arguments.position[1], lat: filter.arguments.position[1],
lon: filter.arguments.position[0], lon: filter.arguments.position[0],
}, },

View File

@@ -25,7 +25,7 @@ export function buildDistanceSort(sort: SCDistanceSort): SortOptions {
mode: 'avg', mode: 'avg',
order: sort.order, order: sort.order,
unit: 'm', unit: 'm',
[`${sort.arguments.field}.point.coordinates`]: { [`${sort.arguments.field}.point`]: {
lat: sort.arguments.position[1], lat: sort.arguments.position[1],
lon: sort.arguments.position[0], lon: sort.arguments.position[0],
}, },

View File

@@ -466,7 +466,7 @@ describe('Query', function () {
const expectedFilter: QueryDslSpecificQueryContainer<'geo_distance'> = { const expectedFilter: QueryDslSpecificQueryContainer<'geo_distance'> = {
geo_distance: { geo_distance: {
'distance': '1000m', 'distance': '1000m',
'geo.point.coordinates': { 'geo.point': {
lat: 8.123, lat: 8.123,
lon: 50.123, lon: 50.123,
}, },
@@ -636,7 +636,7 @@ describe('Query', function () {
'mode': 'avg', 'mode': 'avg',
'order': 'desc', 'order': 'desc',
'unit': 'm', 'unit': 'm',
'geo.point.coordinates': { 'geo.point': {
lat: 50.123, lat: 50.123,
lon: 8.123, lon: 8.123,
}, },

View File

@@ -1,5 +1,11 @@
# @openstapps/tsconfig # @openstapps/tsconfig
## 3.3.0
### Minor Changes
- 688bc5f2: v3.3.0 changes
## 3.0.0 ## 3.0.0
### Major Changes ### Major Changes
@@ -30,7 +36,7 @@
```js ```js
#!/usr/bin/env node #!/usr/bin/env node
import './lib/app.js'; import "./lib/app.js";
``` ```
- 64caebaf: Migrate to ESM - 64caebaf: Migrate to ESM
@@ -105,7 +111,7 @@
```js ```js
#!/usr/bin/env node #!/usr/bin/env node
import './lib/app.js'; import "./lib/app.js";
``` ```
- 64caebaf: Migrate to ESM - 64caebaf: Migrate to ESM

View File

@@ -1,7 +1,7 @@
{ {
"name": "@openstapps/tsconfig", "name": "@openstapps/tsconfig",
"description": "The tsconfig for the openstapps project", "description": "The tsconfig for the openstapps project",
"version": "3.0.0", "version": "3.3.0",
"type": "commonjs", "type": "commonjs",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"repository": "git@gitlab.com:openstapps/eslint-config.git", "repository": "git@gitlab.com:openstapps/eslint-config.git",

View File

@@ -1,12 +1,28 @@
# @openstapps/minimal-connector # @openstapps/minimal-connector
## 3.3.2
### Patch Changes
- @openstapps/core@3.3.2
- @openstapps/api@3.3.2
## 3.3.0
### Patch Changes
- Updated dependencies [688bc5f2]
- @openstapps/api@3.3.0
- @openstapps/core@3.3.0
- @openstapps/logger@3.0.0
## 3.2.0 ## 3.2.0
### Patch Changes ### Patch Changes
- Updated dependencies [912ae422] - Updated dependencies [912ae422]
- @openstapps/core@4.0.0 - @openstapps/core@3.2.0
- @openstapps/api@4.0.0 - @openstapps/api@3.2.0
- @openstapps/logger@3.0.0 - @openstapps/logger@3.0.0
## 3.1.1 ## 3.1.1

View File

@@ -1,7 +1,7 @@
{ {
"name": "@openstapps/minimal-connector", "name": "@openstapps/minimal-connector",
"description": "This is a minimal connector which serves as an example", "description": "This is a minimal connector which serves as an example",
"version": "3.2.0", "version": "3.3.2",
"private": true, "private": true,
"type": "module", "type": "module",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",

View File

@@ -1,13 +1,33 @@
# @openstapps/minimal-plugin # @openstapps/minimal-plugin
## 3.3.2
### Patch Changes
- @openstapps/core@3.3.2
- @openstapps/core-tools@3.3.2
- @openstapps/api-plugin@3.3.2
- @openstapps/api@3.3.2
## 3.3.0
### Patch Changes
- Updated dependencies [688bc5f2]
- @openstapps/api@3.3.0
- @openstapps/core@3.3.0
- @openstapps/api-plugin@3.3.0
- @openstapps/core-tools@3.3.0
- @openstapps/logger@3.0.0
## 3.2.0 ## 3.2.0
### Patch Changes ### Patch Changes
- Updated dependencies [912ae422] - Updated dependencies [912ae422]
- @openstapps/core@4.0.0 - @openstapps/core@3.2.0
- @openstapps/api@4.0.0 - @openstapps/api@3.2.0
- @openstapps/api-plugin@4.0.0 - @openstapps/api-plugin@3.2.0
- @openstapps/core-tools@3.0.0 - @openstapps/core-tools@3.0.0
- @openstapps/logger@3.0.0 - @openstapps/logger@3.0.0

View File

@@ -1,7 +1,7 @@
{ {
"name": "@openstapps/minimal-plugin", "name": "@openstapps/minimal-plugin",
"description": "Minimal Plugin", "description": "Minimal Plugin",
"version": "3.2.0", "version": "3.3.2",
"private": true, "private": true,
"type": "module", "type": "module",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",

142
flake.nix
View File

@@ -4,68 +4,86 @@
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils"; flake-utils.url = "github:numtide/flake-utils";
}; };
outputs = { outputs =
self, {
nixpkgs, self,
flake-utils, nixpkgs,
}: let flake-utils,
aapt2buildToolsVersion = "33.0.2"; }:
in let
flake-utils.lib.eachDefaultSystem (system: let aapt2buildToolsVersion = "33.0.2";
pkgs = import nixpkgs { in
inherit system; flake-utils.lib.eachDefaultSystem (
overlays = [ system:
(final: prev: rec { let
fontMin = prev.python311.withPackages (ps: with ps; [brotli fonttools] ++ (with fonttools.optional-dependencies; [woff])); pkgs = import nixpkgs {
android = prev.androidenv.composeAndroidPackages { inherit system;
buildToolsVersions = ["30.0.3" aapt2buildToolsVersion]; overlays = [
platformVersions = ["33"]; (final: prev: rec {
}; fontMin = prev.python311.withPackages (
cypress = prev.cypress.overrideAttrs (cyPrev: rec { ps:
version = "13.2.0"; with ps;
src = prev.fetchzip { [
url = "https://cdn.cypress.io/desktop/${version}/linux-x64/cypress.zip"; brotli
hash = "sha256-9o0nprGcJhudS1LNm+T7Vf0Dwd1RBauYKI+w1FBQ3ZM="; fonttools
]
++ (with fonttools.optional-dependencies; [ woff ])
);
android = prev.androidenv.composeAndroidPackages {
buildToolsVersions = [
"34.0.0"
aapt2buildToolsVersion
];
platformVersions = [ "34" ];
}; };
}); cypress = prev.cypress.overrideAttrs (cyPrev: rec {
nodejs = prev.nodejs_18; version = "13.2.0";
}) src = prev.fetchzip {
]; url = "https://cdn.cypress.io/desktop/${version}/linux-x64/cypress.zip";
config = { hash = "sha256-9o0nprGcJhudS1LNm+T7Vf0Dwd1RBauYKI+w1FBQ3ZM=";
allowUnfree = true; };
android_sdk.accept_license = true; });
nodejs = prev.nodejs_18;
corepack = prev.corepack_18;
})
];
config = {
allowUnfree = true;
android_sdk.accept_license = true;
};
}; };
}; androidFhs = pkgs.buildFHSUserEnv {
androidFhs = pkgs.buildFHSUserEnv { name = "android-env";
name = "android-env"; targetPkgs = pkgs: with pkgs; [ ];
targetPkgs = pkgs: with pkgs; []; runScript = "bash";
runScript = "bash"; profile = ''
profile = '' export ALLOW_NINJA_ENV=true
export ALLOW_NINJA_ENV=true export USE_CCACHE=1
export USE_CCACHE=1 export LD_LIBRARY_PATH=/usr/lib:/usr/lib32
export LD_LIBRARY_PATH=/usr/lib:/usr/lib32 '';
''; };
}; in
in { {
devShell = pkgs.mkShell rec { devShell = pkgs.mkShell rec {
nativeBuildInputs = [androidFhs]; nativeBuildInputs = [ androidFhs ];
buildInputs = with pkgs; [ buildInputs = with pkgs; [
nodejs nodejs
corepack corepack
# tools # tools
curl curl
jq jq
fontMin fontMin
cypress cypress
# android # android
jdk17 jdk17
android.androidsdk android.androidsdk
]; ];
ANDROID_JAVA_HOME = "${pkgs.jdk.home}"; ANDROID_JAVA_HOME = "${pkgs.jdk.home}";
ANDROID_SDK_ROOT = "${pkgs.android.androidsdk}/libexec/android-sdk"; ANDROID_SDK_ROOT = "${pkgs.android.androidsdk}/libexec/android-sdk";
GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${ANDROID_SDK_ROOT}/build-tools/${aapt2buildToolsVersion}/aapt2"; GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${ANDROID_SDK_ROOT}/build-tools/${aapt2buildToolsVersion}/aapt2";
CYPRESS_INSTALL_BINARY = "0"; CYPRESS_INSTALL_BINARY = "0";
CYPRESS_RUN_BINARY = "${pkgs.cypress}/bin/Cypress"; CYPRESS_RUN_BINARY = "${pkgs.cypress}/bin/Cypress";
}; };
}); }
);
} }

View File

@@ -1,5 +1,38 @@
# @openstapps/app # @openstapps/app
## 3.3.4
### Minor Changes
- 8b581ef9: Use user facing changelogs in the about pages as the primary source, with the technical changes accessible through a sub menu.
## 3.3.3
### Patch Changes
- 496b50d8: Bug fixes and Android target sdk version is now 34
## 3.3.2
### Patch Changes
- 7afc24f1: fixed config version comparison
- @openstapps/core@3.3.2
- @openstapps/api@3.3.2
## 3.3.0
### Minor Changes
- 688bc5f2: v3.3.0 changes
### Patch Changes
- Updated dependencies [688bc5f2]
- @openstapps/api@3.2.0
- @openstapps/core@3.2.0
- @openstapps/collection-utils@3.0.0
## 3.2.0 ## 3.2.0
### Patch Changes ### Patch Changes
@@ -295,3 +328,225 @@
- Updated dependencies [64caebaf] - Updated dependencies [64caebaf]
- @openstapps/api@3.0.0-next.0 - @openstapps/api@3.0.0-next.0
- @openstapps/core@3.0.0-next.0 - @openstapps/core@3.0.0-next.0
## 2.1.1
### Bug Fixes
- autofocus searchbar only when no default data is displayed ([e90286f](https://gitlab.com/openstapps/app/commit/e90286fc6814f5c40af3e297be42f23128b991be))
- breadcrumbs are under parallax ([9e160e8](https://gitlab.com/openstapps/app/commit/9e160e8d1ee9409e4fbe518c9dd9748705e680e1))
- browser logout only if endSession url defined ([7f6de94](https://gitlab.com/openstapps/app/commit/7f6de94ab572be66d7f10758c37dcf10af46b4e0)), closes [#395](https://gitlab.com/openstapps/app/issues/395)
- can't select some list elements on safari 16.4 ([3e99d7f](https://gitlab.com/openstapps/app/commit/3e99d7fa8fcae4538e1afe6a017570b0cb6ff45e))
- canteen view removes item select listener on view exit ([05e996a](https://gitlab.com/openstapps/app/commit/05e996ae9052b11c23dc1093ef526f08e3e2e6b6))
- catalog module semester selection ([afbd1fc](https://gitlab.com/openstapps/app/commit/afbd1fc048997acbc0113f8957016f8947b58626))
- catalog semester selection ([a8c7d5a](https://gitlab.com/openstapps/app/commit/a8c7d5ab5934adf853cc40c1df311178df54057e))
- data detail local favorite fallback causing duplicate nested favorite view ([dff4a95](https://gitlab.com/openstapps/app/commit/dff4a95acc55574b4872b0e4e39555cce0b2fd60))
- data-detail favorite button color leaking to list items ([95f2da2](https://gitlab.com/openstapps/app/commit/95f2da2def39ec56f40a660a63e5f7fddb4b3d53))
- location flow on iOS devices ([f207e02](https://gitlab.com/openstapps/app/commit/f207e029f1b30624bf411a57b3c552ef259e13ed))
- parallax broken since safari 16.4 ([0f970fa](https://gitlab.com/openstapps/app/commit/0f970fa1f164a310e24a85140d8bf0da21e5a5f1))
- remove infinite scroll e2e test ([47565e5](https://gitlab.com/openstapps/app/commit/47565e51b0dda5c8227238b6dc19f0d277408429))
- replace breadcrumb popover with simply expanding the breadcrumbs ([1318cbc](https://gitlab.com/openstapps/app/commit/1318cbca7f0cf72e10d96fff1a1116ba073fe8dc))
- schedule tabs navigating to the wrong url ([abf2ab6](https://gitlab.com/openstapps/app/commit/abf2ab6a5a94941d439adf54ec677332823892db))
- translate simple pipe doesn't update on language changes ([f5ca150](https://gitlab.com/openstapps/app/commit/f5ca1508fb9d95693615bfb9e03bc127bd83be00))
- typo in catalog provider query ([f49c44f](https://gitlab.com/openstapps/app/commit/f49c44f5c53780e4794dc1ef4cbacfb20cabbd97))
- workaround for side menu items not being active on page load ([947cab4](https://gitlab.com/openstapps/app/commit/947cab458ca770f116d28a1f22687ae620e71b59))
### Features
- add content to empty catalogs ([982fb16](https://gitlab.com/openstapps/app/commit/982fb1653b3c1253aac9366733f14c22c94d2537))
- add easy way to configure search filtering for nested properties ([2220ab2](https://gitlab.com/openstapps/app/commit/2220ab24b385188515da7c176bf9c1ac72651fd9))
- dark theme ([e75a466](https://gitlab.com/openstapps/app/commit/e75a46633ca3685d6044eb9a8b2fd670f2cd030f))
- dashboard search rework ([8c30a47](https://gitlab.com/openstapps/app/commit/8c30a47706f07eb222fac47ad7fed61f9044328a))
- implement custom cdk virtual scroll behavior ([968cb72](https://gitlab.com/openstapps/app/commit/968cb729575c529fd6d1d35da1b50a8689057c46))
- optional logout from identity provider ([8cd2d77](https://gitlab.com/openstapps/app/commit/8cd2d777ab3a67b1ab24f03aa50a3ff73550c3d2)), closes [#372](https://gitlab.com/openstapps/app/issues/372)
- revamp dashboard mensa section ([33a74d9](https://gitlab.com/openstapps/app/commit/33a74d96ae92137f53a775e90bff99e5f2d41f6a))
- rework settings page design ([2f1298c](https://gitlab.com/openstapps/app/commit/2f1298c9d7df25d2a16576245ea62c1b6094e507))
- show in-place in date series modal, resolves [#385](https://gitlab.com/openstapps/app/issues/385) ([22e70ae](https://gitlab.com/openstapps/app/commit/22e70ae92b35578b559e6644dccb8d4bfd06af1e)), closes [#386](https://gitlab.com/openstapps/app/issues/386) [#388](https://gitlab.com/openstapps/app/issues/388)
- transition to full sidebar at xl instead of lg ([cc939f3](https://gitlab.com/openstapps/app/commit/cc939f38873833b7cc0260525a2ecd536f27bfa5))
## 2.0.1
### Bug Fixes
- assessment segments can become unreadable ([939fb6e](https://gitlab.com/openstapps/app/commit/939fb6ef0f11b40cb71fbe61da90f50b1f75c3f7))
- login button not easily found ([11d1ac3](https://gitlab.com/openstapps/app/commit/11d1ac3f7ce27c2822ea8f839df3f3dffbd6c020))
- remove misleading assessment calculations ([aefae33](https://gitlab.com/openstapps/app/commit/aefae33d5c9fa9ee3efe346e45429aca79ae3c48))
- right-align add event detail chip ([1eee652](https://gitlab.com/openstapps/app/commit/1eee652533c6b8f613ce09df9c3421f89209419a))
### Features
- offline notice ([9b4caf5](https://gitlab.com/openstapps/app/commit/9b4caf526ffb53ec8d8885342323fcc52fd9fc09))
- separate prettier from eslint ([a88d000](https://gitlab.com/openstapps/app/commit/a88d000ccd6cbdeb5fbb07d209f2491023f9d76c))
# 2.0.0
### Bug Fixes
- daia_url missing in environment configs ([863a3ff](https://gitlab.com/openstapps/app/commit/863a3ffd488425e3313ab9b812c4b6d50c68a244))
- 404 on all surge links ([f1b4930](https://gitlab.com/openstapps/app/commit/f1b4930a3068e73aee20b4c3d71dac551ab60c35))
- add areaServed to person detail ([488150f](https://gitlab.com/openstapps/app/commit/488150f7f5558c05c1ec8a71afcb9f9a37e68a37))
- add event popover expanding beyond screen width ([046a95b](https://gitlab.com/openstapps/app/commit/046a95ba1dca3f5ded7e5555d3167f52f95be107))
- add location info to dates from timetable ([92adb9d](https://gitlab.com/openstapps/app/commit/92adb9dd2db18027dcc327433027e28c81ecbd4b)), closes [#344](https://gitlab.com/openstapps/app/issues/344)
- add missing profile translation ([cdb6ac4](https://gitlab.com/openstapps/app/commit/cdb6ac4084f8704d7f2336387a837b86f78c062b))
- add nav button to schedule page ([e628f39](https://gitlab.com/openstapps/app/commit/e628f396e22e51da2c9f2489fe89e42ccf474e2b))
- add openingHours config and catch its errors ([6125d43](https://gitlab.com/openstapps/app/commit/6125d43e8c18f2bf2afda67c0ff72e00d98ab34f))
- add PKCE parameters for PAIA auth ([f3e83bf](https://gitlab.com/openstapps/app/commit/f3e83bfcc88423f0935a060ccd0bf6198da58351))
- address late init from ionic components ([0bce9e5](https://gitlab.com/openstapps/app/commit/0bce9e5452fc5d05123756348dc30308de675bfa))
- adjust code to overcome the breaking changes (ionic 4 to 5) ([f779042](https://gitlab.com/openstapps/app/commit/f7790426cd2da4a6b33e2aa73783943f45b3de02)), closes [#70](https://gitlab.com/openstapps/app/issues/70)
- adjust hebis catalog templates ([83d9a4a](https://gitlab.com/openstapps/app/commit/83d9a4a8b8fe5b8687c72a717b3a2964524006e0))
- adjust library account user info ([bafabb1](https://gitlab.com/openstapps/app/commit/bafabb1d4ec299e2bea43cd4b8442ef33be2329a)), closes [#331](https://gitlab.com/openstapps/app/issues/331)
- adjust npm docker scripts and typos ([82bb15b](https://gitlab.com/openstapps/app/commit/82bb15b863e2d2e4df20244fda2f2e0d049ff43f))
- angular1 ng-if leftover ([25434d5](https://gitlab.com/openstapps/app/commit/25434d54e3800fd72a6c5d9188fb11f441d73aa9))
- assign navigation app name dynamically ([90b9733](https://gitlab.com/openstapps/app/commit/90b97339d3948b0864f634519416fe4a458b459f))
- background fetch crashing android app ([3316ad9](https://gitlab.com/openstapps/app/commit/3316ad9169ed2b29a2755405589213f824aec9d1))
- calculating SCDateSeries for next unit view ([82ba5f8](https://gitlab.com/openstapps/app/commit/82ba5f81211fb10cc5fde04991856567c4ac9680))
- canteen module layout ([b89f5c4](https://gitlab.com/openstapps/app/commit/b89f5c4edd1ed14f7529edc4e4ea54f9d98fda7c))
- canteen module layout ([6f7c680](https://gitlab.com/openstapps/app/commit/6f7c680ed89f027d863ebc02f5b24895d84f32e4))
- cards not having rounded corners in safari ([8a04a43](https://gitlab.com/openstapps/app/commit/8a04a439032920ead799f8b7483f29b896797c37))
- catch error `Setting "language" not provided` ([584878d](https://gitlab.com/openstapps/app/commit/584878d9503b8406b6ee7ec69dde5b8b3c4fd954))
- CI stage/job setup ([ae429ca](https://gitlab.com/openstapps/app/commit/ae429ca5fb3b5f10cad377d37b251806b3dabf6c))
- config.get issue by updating ionic ([9386351](https://gitlab.com/openstapps/app/commit/93863510fac32ed5b887011175a4807df3f522b8))
- **config:** fix catch ConfigFetchError in getValue ([161da63](https://gitlab.com/openstapps/app/commit/161da630ea59f6205ee53dece950972d4f36ada5)), closes [#46](https://gitlab.com/openstapps/app/issues/46)
- correct data path color ([0d755bc](https://gitlab.com/openstapps/app/commit/0d755bcbd3d6fea59a4f7a59981fd28844ff90d5))
- correct html whitespace handling for icon detection ([9bc3642](https://gitlab.com/openstapps/app/commit/9bc3642990b687dd524470fd26df80351aa85f1e))
- daia availability ([13cee2d](https://gitlab.com/openstapps/app/commit/13cee2d4261c7301c1c763446ae44dcdd005172d))
- dashboard next unit structural directive causing animation issues ([c8f6a27](https://gitlab.com/openstapps/app/commit/c8f6a27c571e51bcc0ac0120968e6bc9a20c8dd7))
- **data:** fix and adjust detail test using translation ([478f49a](https://gitlab.com/openstapps/app/commit/478f49a8744211b3c9458b3dadc791d62a46ae46)), closes [#50](https://gitlab.com/openstapps/app/issues/50)
- **data:** fix template and other consistency issues ([c3bc227](https://gitlab.com/openstapps/app/commit/c3bc227a3ca4a295b2b31bfe7fd34830b33c9f05))
- detail page when dish of mensa selected ([e5c2270](https://gitlab.com/openstapps/app/commit/e5c227073a183a1c562e17f3fe14a51048d01637)), closes [#140](https://gitlab.com/openstapps/app/issues/140)
- download events should respect selection ([28fbfef](https://gitlab.com/openstapps/app/commit/28fbfef18cc3b457f7020a70157ea7a4fff345d0))
- enable background fetch on iOS ([a1592f8](https://gitlab.com/openstapps/app/commit/a1592f84cc48f7cae8c55ef806cddbe806034bb5))
- enable overflow for day labels in schedule ([1195c5f](https://gitlab.com/openstapps/app/commit/1195c5ffc8cea07f1e224d92a7fb25aa5858bf0a))
- encode URI parameter to proxy URI ([bc13cc5](https://gitlab.com/openstapps/app/commit/bc13cc5e1fe6144fe0a0e53c0748a154844a5c29)), closes [#326](https://gitlab.com/openstapps/app/issues/326)
- extend landing period button not working on android ([0caa69c](https://gitlab.com/openstapps/app/commit/0caa69c28cbb2f962b70a1da13659739c1c6dd3e)), closes [#333](https://gitlab.com/openstapps/app/issues/333)
- failing native http requests with body ([fed4f20](https://gitlab.com/openstapps/app/commit/fed4f20c3cf43221512f3d2b6dd3c3fe7a4cf43a))
- feedback not allowing valid emails ([cf74c8e](https://gitlab.com/openstapps/app/commit/cf74c8e19f8bd34a31d5af931781e84be2c04dea)), closes [#349](https://gitlab.com/openstapps/app/issues/349)
- fix issues found by ng build for production ([a503811](https://gitlab.com/openstapps/app/commit/a503811c1cfcf909571af766ccd884856aad8ec9)), closes [#48](https://gitlab.com/openstapps/app/issues/48)
- fix various typos ([ad0dae4](https://gitlab.com/openstapps/app/commit/ad0dae46ff04d28551d2ece950d9a4d3442541d2))
- fixate webdriver to match used chrome version ([24dbb42](https://gitlab.com/openstapps/app/commit/24dbb42b345458b7dbdd17b2759824b3b68cb0e4))
- generate library online links properly ([9854541](https://gitlab.com/openstapps/app/commit/9854541a0c062c31bce167673586dccc8af81785)), closes [#340](https://gitlab.com/openstapps/app/issues/340)
- handle prices as an optional property ([9e71efc](https://gitlab.com/openstapps/app/commit/9e71efca9f7b1086db26f580192d6b349bdcb964)), closes [#219](https://gitlab.com/openstapps/app/issues/219)
- header logo changing size on ios navigate ([38f0a30](https://gitlab.com/openstapps/app/commit/38f0a300769a5b7cda35af0927c17099f93981b8))
- ignore null-island location ([d3188f5](https://gitlab.com/openstapps/app/commit/d3188f50905d610097de6c90bc58e6373d30e0dc)), closes [#149](https://gitlab.com/openstapps/app/issues/149)
- item not available in offer template ([7b402d6](https://gitlab.com/openstapps/app/commit/7b402d61c30aed81a5671d778a38c8393a5bc7c8)), closes [#110](https://gitlab.com/openstapps/app/issues/110)
- library account missing ready for pickup ([e504d8c](https://gitlab.com/openstapps/app/commit/e504d8cf6dd1c12fcb8f6a315527337313662385)), closes [#330](https://gitlab.com/openstapps/app/issues/330)
- library fines should load item title only if needed ([cbb949e](https://gitlab.com/openstapps/app/commit/cbb949e3977a5821e6bd1b654dec66a82e4d8c81)), closes [#342](https://gitlab.com/openstapps/app/issues/342)
- links of timetable tabs ([837c69b](https://gitlab.com/openstapps/app/commit/837c69bb21c92a91259051d5680e1073b390fc0e)), closes [#144](https://gitlab.com/openstapps/app/issues/144)
- logged out button not showing on profile ([ebdc14d](https://gitlab.com/openstapps/app/commit/ebdc14d3c398ac7564c077757c564f4e414fe244)), closes [#239](https://gitlab.com/openstapps/app/issues/239)
- make action chips react to changes of their item ([c0d0b1b](https://gitlab.com/openstapps/app/commit/c0d0b1bd9934e8d9e23f47825cae6a5d8ea2f38a))
- make keyboard dismissable on mobile devices ([b2cc1fd](https://gitlab.com/openstapps/app/commit/b2cc1fd91fc5bd66c994dcbe10771a22d91a1b3e))
- map search on iOS ([7d449b4](https://gitlab.com/openstapps/app/commit/7d449b43d0d86825f711848110ac6f044084eba0)), closes [#148](https://gitlab.com/openstapps/app/issues/148)
- map widget not loading tiles properly ([09aa7bb](https://gitlab.com/openstapps/app/commit/09aa7bb5c59e8d167fa91c69745f5f80229094d7)), closes [#127](https://gitlab.com/openstapps/app/issues/127)
- missing init (config setup) in default auth service ([f16e539](https://gitlab.com/openstapps/app/commit/f16e5394cce5a8019f4dfe367e5e0a2f0cca4ce2)), closes [#227](https://gitlab.com/openstapps/app/issues/227)
- missing translations ([30d801a](https://gitlab.com/openstapps/app/commit/30d801a3b419b382d40d96dda995bd35e493523d))
- modals not reacting after several uses ([f39c29f](https://gitlab.com/openstapps/app/commit/f39c29f10bc05ab986b74dfa8a8648df5fb307b4))
- news module not scrollable on large screens ([44b6a4a](https://gitlab.com/openstapps/app/commit/44b6a4aea008ca6c89f6cb289467429fb3f8c1fa))
- ngx-translate defaultLanguage not set ([581a5b2](https://gitlab.com/openstapps/app/commit/581a5b2e55ceda99cf7c41200366c3c5e09f1c63))
- omit sync native calendar when no uuids ([c9720dc](https://gitlab.com/openstapps/app/commit/c9720dc104cce78ae1a422d5efed7b8a58946836)), closes [#177](https://gitlab.com/openstapps/app/issues/177)
- opening hours pipe refreshing too often ([95e1734](https://gitlab.com/openstapps/app/commit/95e1734d26b175d8d1156abb10863155fed89ec0))
- overhaul auth to avoid issues ([99e8d6c](https://gitlab.com/openstapps/app/commit/99e8d6c9bcbc68b639b035af36bc05de0237b1f9)), closes [#336](https://gitlab.com/openstapps/app/issues/336)
- overview search bar scrolling behind header ([a037090](https://gitlab.com/openstapps/app/commit/a037090eec06e867a703b88a43620a74770287fe))
- performance degradation when scrolling ([f0a45d1](https://gitlab.com/openstapps/app/commit/f0a45d1b8eb2b33a6c68b94ed7f96f81db3a728b))
- person list should use long-inline-text ([8b2b853](https://gitlab.com/openstapps/app/commit/8b2b853942ac76904ff49d940dfef625b0397150))
- prevent multiple heavy setting inits ([f772637](https://gitlab.com/openstapps/app/commit/f7726378f443d9809a6174411d25a811e1d0b5e9))
- prevent opening invalid links ([fdee2db](https://gitlab.com/openstapps/app/commit/fdee2db8a42b8f6c99c4a72b3104ae0ba1a41c5a)), closes [#328](https://gitlab.com/openstapps/app/issues/328)
- profile module items show click effect on scroll ([8b2f2c0](https://gitlab.com/openstapps/app/commit/8b2f2c063c65278580d9469b00d9e67f01caaffb))
- recurring schedule offset and event limit ([9c6b513](https://gitlab.com/openstapps/app/commit/9c6b5131cd4cadb2572769d368b346054f19de1c))
- refresh token not used by default auth provider ([15ccbe4](https://gitlab.com/openstapps/app/commit/15ccbe4c1879417f2fc5849204fa9f6bdcce87fc)), closes [#311](https://gitlab.com/openstapps/app/issues/311)
- remove "extend landing" button when renewal not possible ([f60a228](https://gitlab.com/openstapps/app/commit/f60a22839200019a38586a14a0cce32e2c0029e7)), closes [#338](https://gitlab.com/openstapps/app/issues/338)
- remove item before adding it to secure storage ([ec511fb](https://gitlab.com/openstapps/app/commit/ec511fb8f40219e2559b08c62bd915d773d2a36f))
- schedule navigation bar layout ([e7d5f83](https://gitlab.com/openstapps/app/commit/e7d5f83100f43564b55249909a6658e583e3a9b2))
- set android status bar color correctly ([b38a969](https://gitlab.com/openstapps/app/commit/b38a96996a10e4e43ff1b06ecd2235a0e3bdfa1c))
- setting of default language ([ccf8b1a](https://gitlab.com/openstapps/app/commit/ccf8b1a5cc9fe834ba3e04a1ed67b2d082004403))
- show nothing for empty array properties ([1c56c89](https://gitlab.com/openstapps/app/commit/1c56c891e15b034826b5d6b2b35141fc872c922d)), closes [#154](https://gitlab.com/openstapps/app/issues/154)
- single map place width ([88684f0](https://gitlab.com/openstapps/app/commit/88684f068ab130f43e520c87f1d7383e0ae43944)), closes [#147](https://gitlab.com/openstapps/app/issues/147)
- some android devices don't support implied css animation units ([54cc883](https://gitlab.com/openstapps/app/commit/54cc8838aefe8f8c2d25d9228a136ef727a08230))
- status bar being black on Android 13 devices ([feee9e8](https://gitlab.com/openstapps/app/commit/feee9e8db90e66cf2346f7c5cc24f075eb70676c))
- suppress router event logs in console ([28caaf1](https://gitlab.com/openstapps/app/commit/28caaf1d21f7961b678cf339a712abf860ade5e7)), closes [#207](https://gitlab.com/openstapps/app/issues/207)
- swap missing icon after ionic update ([643b6c9](https://gitlab.com/openstapps/app/commit/643b6c967f3268cb305a24d614c3bc91275b0ac3))
- temporary disable flaky ui test ([6b9b1fa](https://gitlab.com/openstapps/app/commit/6b9b1fa8548d5c5fca04b2c1d63e893de39278a2))
- timetable dates cannot be removed ([9242438](https://gitlab.com/openstapps/app/commit/924243813207fa791d3c4938f8653a999b6382ff))
- translate back button title ([b8db0f3](https://gitlab.com/openstapps/app/commit/b8db0f3e70a46f2b493e183a244cb29d1954c257))
- translations ([5e1a902](https://gitlab.com/openstapps/app/commit/5e1a902d4c0d2345f21500fba5394c1907e04eb8))
- typo in translation ([7928534](https://gitlab.com/openstapps/app/commit/7928534d88a26db28b098bbceb47bc1103022a57))
- update core and apply stricter tslint rules ([911492d](https://gitlab.com/openstapps/app/commit/911492d064ff0280dd6626244cd8038cbfc0f408))
- use HashLocationStrategy for routes ([9d68212](https://gitlab.com/openstapps/app/commit/9d682125db55c87cab2b33c7633bfa133d2fc5a9)), closes [#54](https://gitlab.com/openstapps/app/issues/54)
- use localized date and time ([6ca0b97](https://gitlab.com/openstapps/app/commit/6ca0b9763761c5204a757a243056a087c5f35fd9))
- use stapps core version to compare with backends' core version ([66b8720](https://gitlab.com/openstapps/app/commit/66b8720da0f264824a396f2d9e598b0e48c8e3d1)), closes [#77](https://gitlab.com/openstapps/app/issues/77)
- user info card ([998edcb](https://gitlab.com/openstapps/app/commit/998edcb5cdfb588c2986f466f4a2951f172a8bb4)), closes [#305](https://gitlab.com/openstapps/app/issues/305)
### Features
- add "no results" screen to search ([c75ca68](https://gitlab.com/openstapps/app/commit/c75ca68c440a20e197213ecbb47d05fc293afd9c))
- add about module ([d420008](https://gitlab.com/openstapps/app/commit/d42000892694f4a3b29fa623c43fb45f8ba54687))
- add action chips to search results ([67fb4a4](https://gitlab.com/openstapps/app/commit/67fb4a43c95043caba50d43ace2ab276f3319b81))
- add auth support (default and paia) ([b5f239e](https://gitlab.com/openstapps/app/commit/b5f239ea4edebd0d27b1cdaad4a830998ce2f681))
- add backend toggle ([c1d3330](https://gitlab.com/openstapps/app/commit/c1d33303aa11da3b3e150c6717d77ef484a16ac1))
- add basic templates for data list items ([3e697b1](https://gitlab.com/openstapps/app/commit/3e697b17b4448c15781d0f6dd55577b638e9d974))
- add catalog module ([03084b1](https://gitlab.com/openstapps/app/commit/03084b1c966de98b3723d0bee2b2475589393c59))
- add ConfigModule and FakeBackendInterceptor ([406f400](https://gitlab.com/openstapps/app/commit/406f40055567bfde4ec5edf26cff942411bd073e)), closes [#34](https://gitlab.com/openstapps/app/issues/34) [#37](https://gitlab.com/openstapps/app/issues/37)
- add detail view for news ([2566a71](https://gitlab.com/openstapps/app/commit/2566a71a81a3408dbb16b97d3a453d95e25d1f00))
- add duration pipe with frequency capabilites ([49a1758](https://gitlab.com/openstapps/app/commit/49a1758da358958ffe590700c19aaf90ec748ee5))
- add favorites support ([e9452d6](https://gitlab.com/openstapps/app/commit/e9452d6520c34f6513623c16e291dc23d6ea9757))
- add feedback module ([867f9e9](https://gitlab.com/openstapps/app/commit/867f9e9b832e3bd54c04801fef63a11543e8f3dd))
- add filter chips for news ([5435f85](https://gitlab.com/openstapps/app/commit/5435f85cc43dc3baa774a5008d2920ac7b3783f6))
- add formatting pipes basted on Intl ([4b932af](https://gitlab.com/openstapps/app/commit/4b932af1c07e1af4369414667a987d31181c073c))
- add HeBIS HDS search ([9a3241c](https://gitlab.com/openstapps/app/commit/9a3241c42ab59e15c0084178f76dc4a2450a2bb8))
- add library account screens ([080e6fa](https://gitlab.com/openstapps/app/commit/080e6fa3e8c18e9608d7fa2efc95e4fd65c43a15))
- add library action confirmations ([42b860e](https://gitlab.com/openstapps/app/commit/42b860e41793fc3983a39237a4f7128416485fae)), closes [#334](https://gitlab.com/openstapps/app/issues/334)
- add logger ([a0c798f](https://gitlab.com/openstapps/app/commit/a0c798f765d87c5eebcbed65b70f3b05f285d0ce))
- add map module ([c1c9a92](https://gitlab.com/openstapps/app/commit/c1c9a92ec900403218b887fdebfe5132b232e1e0))
- add new font and new icons ([915fd72](https://gitlab.com/openstapps/app/commit/915fd72bd4bfed16e15fcc3c57879db0ec0379e2))
- add not found screen ([e3d9ef4](https://gitlab.com/openstapps/app/commit/e3d9ef40ccd626c81c67ea2d790eecbe6e025780))
- add permission check for geoLocation setting ([d5fa2fd](https://gitlab.com/openstapps/app/commit/d5fa2fd9a578d48cd2513eeb1380f1d2bc4d3754))
- add service and pipe for core translator ([4565600](https://gitlab.com/openstapps/app/commit/456560026cc9550a10a9f42657d942122be34d53))
- apply new layout overhaul ([7bbdba5](https://gitlab.com/openstapps/app/commit/7bbdba5c0b886e2789d2a603c4be627dfd16b60e))
- assessment tree view ([0b037f9](https://gitlab.com/openstapps/app/commit/0b037f96e634b412fbaaee24747df08afdc0e565))
- assessments module ([e68d1b7](https://gitlab.com/openstapps/app/commit/e68d1b73f94b36abcefe9b2bf42e98de00b69188))
- calendar plugin ([a57c302](https://gitlab.com/openstapps/app/commit/a57c3029df61ac3157c856744380a85dc001abc6))
- dashboard ui tests ([9f8ab5c](https://gitlab.com/openstapps/app/commit/9f8ab5c7a15a918f7bd05423f0a43f22a33d9228))
- **data:** add basic methods of data provider ([ffe05e4](https://gitlab.com/openstapps/app/commit/ffe05e4548fc399183ef651047cb02a3cdc80c67)), closes [#1](https://gitlab.com/openstapps/app/issues/1)
- **data:** add data detail templates ([5855acc](https://gitlab.com/openstapps/app/commit/5855accc169579d87f5779fd602d4f00f2b479a1))
- **data:** add method that checks if data item has been saved ([017fc67](https://gitlab.com/openstapps/app/commit/017fc67765bdb75542045830fb85f8eb4b899485))
- **data:** implement basic facets handling ([b6f92a7](https://gitlab.com/openstapps/app/commit/b6f92a74494e1a39d5c828d593eb70c7002bb0f6)), closes [#1](https://gitlab.com/openstapps/app/issues/1)
- **data:** show skeleton screens before data is loaded ([e1039aa](https://gitlab.com/openstapps/app/commit/e1039aa2260a0e9d8ccca5a18ded480bf2f12530)), closes [#4](https://gitlab.com/openstapps/app/issues/4)
- **data:** use data provider for list and detail view ([7caaa18](https://gitlab.com/openstapps/app/commit/7caaa18b7eb94ed8e4ff4e54d760ba1dba3ae3be))
- **data:** use general template for all offers ([58960a2](https://gitlab.com/openstapps/app/commit/58960a29ea0cd73f26a43186b41f5b53864810e0))
- display availability and item data for library items ([d571b1d](https://gitlab.com/openstapps/app/commit/d571b1dbe59f8e2270d88dd050b94283bf0a7056))
- dynamic news page rows ([848d257](https://gitlab.com/openstapps/app/commit/848d2574c7046d6f84b91e32aa83ca57fca8ad2e))
- expandable accordion in grades module ([3f81afd](https://gitlab.com/openstapps/app/commit/3f81afda82cf87bb393fbb3b71d27eee77ae42d9))
- get tab navigation items from config ([c3130a3](https://gitlab.com/openstapps/app/commit/c3130a392a53c9ec3657e24a53ed0b3333ba162b))
- Implement variable for styling ([8ecf347](https://gitlab.com/openstapps/app/commit/8ecf347c9a8abac9347dca45e99ebe35eca8f252))
- include font licenses in the licenses section ([82479f4](https://gitlab.com/openstapps/app/commit/82479f463cbee834507a0c8faf895a729631eb06))
- Ionic v6 breadcrumbs in catalog module ([7b491ed](https://gitlab.com/openstapps/app/commit/7b491ed3bb5466a845493bd8ea0bbb836a4f03d2))
- lazy load all news ([e48134e](https://gitlab.com/openstapps/app/commit/e48134eddcd1ca4d5ec5dbf910571e33a3311ba1))
- **menu:** add context menu ([1dbf451](https://gitlab.com/openstapps/app/commit/1dbf4515fe57cc8250a7fa2213ced682e3e5e0fc)), closes [#3](https://gitlab.com/openstapps/app/issues/3)
- navigation rail ([6b08af6](https://gitlab.com/openstapps/app/commit/6b08af6a746bf12005d3297ec97c130e84477615))
- news module ([22cd0af](https://gitlab.com/openstapps/app/commit/22cd0af1bf92a4576316f5510c22f012e085a237))
- profile page sections ([e395e9d](https://gitlab.com/openstapps/app/commit/e395e9d270f41bd4f6e5ecd88e438a28dde92465)), closes [#233](https://gitlab.com/openstapps/app/issues/233) [#261](https://gitlab.com/openstapps/app/issues/261) [#267](https://gitlab.com/openstapps/app/issues/267)
- refresh on pull for news module ([1f3d9ad](https://gitlab.com/openstapps/app/commit/1f3d9ad5f0c0557add2dcf242cf0b7dd7685bb1b))
- resume at origin path after login ([a5e5a5b](https://gitlab.com/openstapps/app/commit/a5e5a5b40799e7505557f7ebd764b9c563be0f4b)), closes [#168](https://gitlab.com/openstapps/app/issues/168)
- schedule layout ([e416590](https://gitlab.com/openstapps/app/commit/e4165901bb5efa6b38e397cdf5d66138e396d7f2))
- scroll schedule cursor into view ([bc4c3d7](https://gitlab.com/openstapps/app/commit/bc4c3d78dbd906243dcddac4db9ac8ccaca79056))
- search url query param handling ([f349bd7](https://gitlab.com/openstapps/app/commit/f349bd72335c47d292d0a007b1a4ce7f5c694a61))
- seperate dishes by menu sections ([400c6b8](https://gitlab.com/openstapps/app/commit/400c6b8d8c5300035862186096e38883f781d297))
- show availability in offers ([5fdef95](https://gitlab.com/openstapps/app/commit/5fdef95c065e7b467a13045adbb56a641cb2dc12))
- show feedback when map search yields no results ([c54ea86](https://gitlab.com/openstapps/app/commit/c54ea867bd64af187446dbf8dc03967aae600961))
- show menu for multiple days for canteens and cafes ([3c079cd](https://gitlab.com/openstapps/app/commit/3c079cd189e3b75d3b203bd46ab57378f99e88cc)), closes [#19](https://gitlab.com/openstapps/app/issues/19) [#79](https://gitlab.com/openstapps/app/issues/79)
- **storage:** add search using regex ([86b9bff](https://gitlab.com/openstapps/app/commit/86b9bff09a51b17151efa5ec322cddcc50a54cb2))
- **storage:** support deletion of multiple entries ([63266f5](https://gitlab.com/openstapps/app/commit/63266f588f6ddb2476e2cea4bab42a3864f77470))
- **storage:** support search using a string ([4334cad](https://gitlab.com/openstapps/app/commit/4334cad68c7d0abb7443e245b6eb983804547925))
- support deep links ([2e3f668](https://gitlab.com/openstapps/app/commit/2e3f6684ef5fbac8e4fb127c536b2b438399ce37))
- support overlapping timetable dates ([93c37b2](https://gitlab.com/openstapps/app/commit/93c37b26cca7764dea66fb12c0e53acc8fd78d2b))
- tab navigation bar animations and state ([7ecba0b](https://gitlab.com/openstapps/app/commit/7ecba0b7819ae5a7ab32d86f6049de0ab6c68e55))
- timetable module - schedule and calendar ([d8ede00](https://gitlab.com/openstapps/app/commit/d8ede006dfbd613dfbc752eb6de80db2e7e84531))
- turn on oauth2 state check for PAIA ([5bd0b50](https://gitlab.com/openstapps/app/commit/5bd0b50816973548a0a4aa5dbed3f2d0902590dd)), closes [#172](https://gitlab.com/openstapps/app/issues/172)
- use http interceptor for backendless development ([2558163](https://gitlab.com/openstapps/app/commit/2558163ad6c3038445a79e6338f1827cb1e6510e)), closes [#37](https://gitlab.com/openstapps/app/issues/37)
- use school-neutral news image fallback via css ([bb94c71](https://gitlab.com/openstapps/app/commit/bb94c71761ca5d3bf6639476ba0c143678cfabbd))
- webpack bundle analyzer ([552911c](https://gitlab.com/openstapps/app/commit/552911cfcfecec8d9be8b8bbb155b597d1f70fa2))
## 0.0.1
### Features
- add the app ([8b23159](https://gitlab.com/openstapps/app/commit/8b23159e678773b08252bc5826510de2a302fa1d))

View File

@@ -13,25 +13,25 @@ The StApps 1.x.x (legacy app, but current app in stores) is written using Ionic
There are (`npm`) scripts defined to get the app running as quickly as possible. Those scripts (shortcuts for docker commands) are called using the syntax `npm run + <script-name>`. So we have the following commands available: There are (`npm`) scripts defined to get the app running as quickly as possible. Those scripts (shortcuts for docker commands) are called using the syntax `npm run + <script-name>`. So we have the following commands available:
``` ```sh
npm run docker:pull npm run docker:pull
``` ```
which pulls the up-to-date image ([Dockerfile](Dockerfile)) which contains all the tools needed for building, serving and deploying the app. which pulls the up-to-date image ([Dockerfile](Dockerfile)) which contains all the tools needed for building, serving and deploying the app.
``` ```sh
npm run docker:enter npm run docker:enter
``` ```
which enters the container on docker builder image, where we can run `npm install` (to install the required npm packages) and `npm build` (to build the app: convert into executable files), but also any other arbitrary commands with the tools available in the docker image. which enters the container on docker builder image, where we can run `npm install` (to install the required npm packages) and `npm build` (to build the app: convert into executable files), but also any other arbitrary commands with the tools available in the docker image.
``` ```sh
npm run docker:build npm run docker:build
``` ```
which runs `npm install` (to install the required npm packages) and `npm build` (to build the app: convert into executable files) in the docker container which runs on the docker builder image. which runs `npm install` (to install the required npm packages) and `npm build` (to build the app: convert into executable files) in the docker container which runs on the docker builder image.
``` ```sh
npm run docker:serve npm run docker:serve
``` ```
@@ -39,7 +39,7 @@ which serves the app for running it in the browser. It basically runs `ionic ser
## How to build and start the app using the default backend? ## How to build and start the app using the default backend?
``` ```sh
npm run build npm run build
npm run start npm run start
``` ```
@@ -86,52 +86,98 @@ addToIonicDB(
You'll need to run _Chromium_ using You'll need to run _Chromium_ using
```shell ```sh
pnpm chromium:no-cors pnpm chromium:no-cors
``` ```
### Help, I can't log in! ### Help, I can't log in!
Login services will often block hosts not coming from the production Login services will often block hosts not coming from the production
server. You can circumvent this locally by using the `:virtual-host` server.
scripts:
```shell #### Web
On the web you can circumvent this locally by using the `:virtual-host` scripts:
```sh
# Start the dev server on mobile.app.uni-frankfurt.de # Start the dev server on mobile.app.uni-frankfurt.de
pnpm start:virtual-host pnpm start:virtual-host
# Run chromium with flags that redirect mobile.app.uni-frankfurt.de to localhost:8100 # Run chromium with flags that redirect mobile.app.uni-frankfurt.de to localhost:8100
pnpm chromium:virtual-host pnpm chromium:virtual-host
``` ```
#### Android
On Android you will need to change the `custom_url_scheme` values
to `de.unifrankfurt.app` in the following files:
- `android/app/src/main/res/values/strings.xml`
- `src/environment/environment.ts`
Then start the app normally as you would
```sh
pnpm run:android
```
**This alone will not make auth work**, only the login flow.
If you need to test login, you have to disable live reload:
```sh
pnpm ionic capacitor run android
```
_**CAUTION:** a remote chrome debugging session can in some
cases hijack the device's ADB connection. If the connection
fails for no obvious reason, close chrome and uninstall the
app, then try again._
#### iOS
On Android you will need to change the `custom_url_scheme` value in
`src/environment/environment.ts` as well as the `CFBundleURLTypes`
in `ios/App/App/Info.plist`.
- make sure to remove any `Info.plist.orig` as capacitor might override
the modified `Info.plist` with that.
- make sure you have a valid device in XCode (Simulator or real device).
After that, run
```sh
pnpm run:ios
```
### Running the app ### Running the app
Install the npm packages needed for running the app (as for any other node project which uses npm): Install the npm packages needed for running the app (as for any other node project which uses npm):
``` ```sh
npm install npm install
``` ```
Check the code for linter issues: Check the code for linter issues:
``` ```sh
npm run lint npm run lint
``` ```
Automatically fix linter issues (those where autofix is possible): Automatically fix linter issues (those where autofix is possible):
``` ```sh
npm run lint:fix npm run lint:fix
``` ```
Build the app (transpile etc.): Build the app (transpile etc.):
``` ```sh
npm run build npm run build
``` ```
Open the app in the browser: Open the app in the browser:
``` ```sh
ionic serve ionic serve
``` ```
@@ -139,7 +185,7 @@ ionic serve
Run the app for testing on an android device (with live reload in the webview / device, when files are changed): Run the app for testing on an android device (with live reload in the webview / device, when files are changed):
``` ```sh
npm run build # if needed npm run build # if needed
npm run resources:android # generate needed resources (icons and splashscreens) npm run resources:android # generate needed resources (icons and splashscreens)
npm run docker:run:android # runs "ionic capacitor run android --livereload --external" on a selected device npm run docker:run:android # runs "ionic capacitor run android --livereload --external" on a selected device
@@ -159,7 +205,7 @@ Besides that, it is possible to monitor processes (and so the processes related
Build the (debug) app for testing on an android device (creates an APK file in the android's build outputs path): Build the (debug) app for testing on an android device (creates an APK file in the android's build outputs path):
``` ```sh
npm run docker:build:android npm run docker:build:android
``` ```
@@ -169,13 +215,13 @@ The mentioned `docker:*:android` npm commands are executed in a docker container
Execute unit tests: Execute unit tests:
``` ```sh
npm test npm test
``` ```
Execute e2e tests: Execute e2e tests:
``` ```sh
npm run e2e npm run e2e
``` ```

View File

@@ -2,7 +2,7 @@ apply plugin: 'com.android.application'
android { android {
namespace "de.anyschool.app" namespace "de.anyschool.app"
compileSdkVersion rootProject.ext.compileSdkVersion compileSdk rootProject.ext.compileSdkVersion
defaultConfig { defaultConfig {
applicationId "de.anyschool.app" applicationId "de.anyschool.app"
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion

View File

@@ -9,6 +9,7 @@ android {
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies { dependencies {
implementation project(':capacitor-community-screen-brightness')
implementation project(':capacitor-app') implementation project(':capacitor-app')
implementation project(':capacitor-browser') implementation project(':capacitor-browser')
implementation project(':capacitor-clipboard') implementation project(':capacitor-clipboard')
@@ -21,6 +22,7 @@ dependencies {
implementation project(':capacitor-local-notifications') implementation project(':capacitor-local-notifications')
implementation project(':capacitor-network') implementation project(':capacitor-network')
implementation project(':capacitor-preferences') implementation project(':capacitor-preferences')
implementation project(':capacitor-screen-orientation')
implementation project(':capacitor-share') implementation project(':capacitor-share')
implementation project(':capacitor-splash-screen') implementation project(':capacitor-splash-screen')
implementation project(':transistorsoft-capacitor-background-fetch') implementation project(':transistorsoft-capacitor-background-fetch')

View File

@@ -1,4 +1,8 @@
[ [
{
"pkg": "@capacitor-community/screen-brightness",
"classpath": "com.elylucas.capscreenbrightness.ScreenBrightnessPlugin"
},
{ {
"pkg": "@capacitor/app", "pkg": "@capacitor/app",
"classpath": "com.capacitorjs.plugins.app.AppPlugin" "classpath": "com.capacitorjs.plugins.app.AppPlugin"
@@ -47,6 +51,10 @@
"pkg": "@capacitor/preferences", "pkg": "@capacitor/preferences",
"classpath": "com.capacitorjs.plugins.preferences.PreferencesPlugin" "classpath": "com.capacitorjs.plugins.preferences.PreferencesPlugin"
}, },
{
"pkg": "@capacitor/screen-orientation",
"classpath": "com.capacitorjs.plugins.screenorientation.ScreenOrientationPlugin"
},
{ {
"pkg": "@capacitor/share", "pkg": "@capacitor/share",
"classpath": "com.capacitorjs.plugins.share.SharePlugin" "classpath": "com.capacitorjs.plugins.share.SharePlugin"

View File

@@ -7,8 +7,8 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:8.0.0' classpath 'com.android.tools.build:gradle:8.2.1'
classpath 'com.google.gms:google-services:4.3.15' classpath 'com.google.gms:google-services:4.4.0'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files

View File

@@ -1,51 +1,57 @@
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN // DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
include ':capacitor-android' include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../../../node_modules/.pnpm/@capacitor+android@5.5.0_@capacitor+core@5.5.0/node_modules/@capacitor/android/capacitor') project(':capacitor-android').projectDir = new File('../../../node_modules/.pnpm/@capacitor+android@6.1.1_@capacitor+core@6.1.1/node_modules/@capacitor/android/capacitor')
include ':capacitor-community-screen-brightness'
project(':capacitor-community-screen-brightness').projectDir = new File('../../../node_modules/.pnpm/@capacitor-community+screen-brightness@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor-community/screen-brightness/android')
include ':capacitor-app' include ':capacitor-app'
project(':capacitor-app').projectDir = new File('../../../node_modules/.pnpm/@capacitor+app@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/app/android') project(':capacitor-app').projectDir = new File('../../../node_modules/.pnpm/@capacitor+app@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/app/android')
include ':capacitor-browser' include ':capacitor-browser'
project(':capacitor-browser').projectDir = new File('../../../node_modules/.pnpm/@capacitor+browser@5.1.0_@capacitor+core@5.5.0/node_modules/@capacitor/browser/android') project(':capacitor-browser').projectDir = new File('../../../node_modules/.pnpm/@capacitor+browser@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/browser/android')
include ':capacitor-clipboard' include ':capacitor-clipboard'
project(':capacitor-clipboard').projectDir = new File('../../../node_modules/.pnpm/@capacitor+clipboard@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/clipboard/android') project(':capacitor-clipboard').projectDir = new File('../../../node_modules/.pnpm/@capacitor+clipboard@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/clipboard/android')
include ':capacitor-device' include ':capacitor-device'
project(':capacitor-device').projectDir = new File('../../../node_modules/.pnpm/@capacitor+device@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/device/android') project(':capacitor-device').projectDir = new File('../../../node_modules/.pnpm/@capacitor+device@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/device/android')
include ':capacitor-dialog' include ':capacitor-dialog'
project(':capacitor-dialog').projectDir = new File('../../../node_modules/.pnpm/@capacitor+dialog@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/dialog/android') project(':capacitor-dialog').projectDir = new File('../../../node_modules/.pnpm/@capacitor+dialog@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/dialog/android')
include ':capacitor-filesystem' include ':capacitor-filesystem'
project(':capacitor-filesystem').projectDir = new File('../../../node_modules/.pnpm/@capacitor+filesystem@5.1.4_@capacitor+core@5.5.0/node_modules/@capacitor/filesystem/android') project(':capacitor-filesystem').projectDir = new File('../../../node_modules/.pnpm/@capacitor+filesystem@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/filesystem/android')
include ':capacitor-geolocation' include ':capacitor-geolocation'
project(':capacitor-geolocation').projectDir = new File('../../../node_modules/.pnpm/@capacitor+geolocation@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/geolocation/android') project(':capacitor-geolocation').projectDir = new File('../../../node_modules/.pnpm/@capacitor+geolocation@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/geolocation/android')
include ':capacitor-haptics' include ':capacitor-haptics'
project(':capacitor-haptics').projectDir = new File('../../../node_modules/.pnpm/@capacitor+haptics@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/haptics/android') project(':capacitor-haptics').projectDir = new File('../../../node_modules/.pnpm/@capacitor+haptics@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/haptics/android')
include ':capacitor-keyboard' include ':capacitor-keyboard'
project(':capacitor-keyboard').projectDir = new File('../../../node_modules/.pnpm/@capacitor+keyboard@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/keyboard/android') project(':capacitor-keyboard').projectDir = new File('../../../node_modules/.pnpm/@capacitor+keyboard@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/keyboard/android')
include ':capacitor-local-notifications' include ':capacitor-local-notifications'
project(':capacitor-local-notifications').projectDir = new File('../../../node_modules/.pnpm/@capacitor+local-notifications@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/local-notifications/android') project(':capacitor-local-notifications').projectDir = new File('../../../node_modules/.pnpm/@capacitor+local-notifications@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/local-notifications/android')
include ':capacitor-network' include ':capacitor-network'
project(':capacitor-network').projectDir = new File('../../../node_modules/.pnpm/@capacitor+network@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/network/android') project(':capacitor-network').projectDir = new File('../../../node_modules/.pnpm/@capacitor+network@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/network/android')
include ':capacitor-preferences' include ':capacitor-preferences'
project(':capacitor-preferences').projectDir = new File('../../../node_modules/.pnpm/@capacitor+preferences@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/preferences/android') project(':capacitor-preferences').projectDir = new File('../../../node_modules/.pnpm/@capacitor+preferences@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/preferences/android')
include ':capacitor-screen-orientation'
project(':capacitor-screen-orientation').projectDir = new File('../../../node_modules/.pnpm/@capacitor+screen-orientation@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/screen-orientation/android')
include ':capacitor-share' include ':capacitor-share'
project(':capacitor-share').projectDir = new File('../../../node_modules/.pnpm/@capacitor+share@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/share/android') project(':capacitor-share').projectDir = new File('../../../node_modules/.pnpm/@capacitor+share@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/share/android')
include ':capacitor-splash-screen' include ':capacitor-splash-screen'
project(':capacitor-splash-screen').projectDir = new File('../../../node_modules/.pnpm/@capacitor+splash-screen@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/splash-screen/android') project(':capacitor-splash-screen').projectDir = new File('../../../node_modules/.pnpm/@capacitor+splash-screen@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/splash-screen/android')
include ':transistorsoft-capacitor-background-fetch' include ':transistorsoft-capacitor-background-fetch'
project(':transistorsoft-capacitor-background-fetch').projectDir = new File('../../../node_modules/.pnpm/@transistorsoft+capacitor-background-fetch@5.1.1_@capacitor+core@5.5.0/node_modules/@transistorsoft/capacitor-background-fetch/android') project(':transistorsoft-capacitor-background-fetch').projectDir = new File('../../../node_modules/.pnpm/@transistorsoft+capacitor-background-fetch@5.2.0_@capacitor+core@6.1.1/node_modules/@transistorsoft/capacitor-background-fetch/android')
include ':capacitor-secure-storage-plugin' include ':capacitor-secure-storage-plugin'
project(':capacitor-secure-storage-plugin').projectDir = new File('../../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@5.5.0/node_modules/capacitor-secure-storage-plugin/android') project(':capacitor-secure-storage-plugin').projectDir = new File('../../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@6.1.1/node_modules/capacitor-secure-storage-plugin/android')

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-all.zip
networkTimeout=10000 networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -1,14 +1,14 @@
ext { ext {
minSdkVersion = 22 minSdkVersion = 22
compileSdkVersion = 33 compileSdkVersion = 34
targetSdkVersion = 33 targetSdkVersion = 34
androidxActivityVersion = '1.7.0' androidxActivityVersion = '1.8.0'
androidxAppCompatVersion = '1.6.1' androidxAppCompatVersion = '1.6.1'
androidxCoordinatorLayoutVersion = '1.2.0' androidxCoordinatorLayoutVersion = '1.2.0'
androidxCoreVersion = '1.10.0' androidxCoreVersion = '1.12.0'
androidxFragmentVersion = '1.5.6' androidxFragmentVersion = '1.6.2'
coreSplashScreenVersion = '1.0.0' coreSplashScreenVersion = '1.0.1'
androidxWebkitVersion = '1.6.1' androidxWebkitVersion = '1.9.0'
junitVersion = '4.13.2' junitVersion = '4.13.2'
androidxJunitVersion = '1.1.5' androidxJunitVersion = '1.1.5'
androidxEspressoCoreVersion = '3.5.1' androidxEspressoCoreVersion = '3.5.1'

View File

@@ -35,6 +35,11 @@
"glob": "**/*", "glob": "**/*",
"input": "src/assets", "input": "src/assets",
"output": "assets" "output": "assets"
},
{
"glob": "CHANGELOG.md",
"input": ".",
"output": "assets"
} }
], ],
"styles": [ "styles": [

View File

@@ -1,4 +1,4 @@
require_relative '../../../../node_modules/.pnpm/@capacitor+ios@5.5.0_@capacitor+core@5.5.0/node_modules/@capacitor/ios/scripts/pods_helpers' require_relative '../../../../node_modules/.pnpm/@capacitor+ios@6.1.1_@capacitor+core@6.1.1/node_modules/@capacitor/ios/scripts/pods_helpers'
platform :ios, '13.0' platform :ios, '13.0'
use_frameworks! use_frameworks!
@@ -9,24 +9,26 @@ use_frameworks!
install! 'cocoapods', :disable_input_output_paths => true install! 'cocoapods', :disable_input_output_paths => true
def capacitor_pods def capacitor_pods
pod 'Capacitor', :path => '../../../../node_modules/.pnpm/@capacitor+ios@5.5.0_@capacitor+core@5.5.0/node_modules/@capacitor/ios' pod 'Capacitor', :path => '../../../../node_modules/.pnpm/@capacitor+ios@6.1.1_@capacitor+core@6.1.1/node_modules/@capacitor/ios'
pod 'CapacitorCordova', :path => '../../../../node_modules/.pnpm/@capacitor+ios@5.5.0_@capacitor+core@5.5.0/node_modules/@capacitor/ios' pod 'CapacitorCordova', :path => '../../../../node_modules/.pnpm/@capacitor+ios@6.1.1_@capacitor+core@6.1.1/node_modules/@capacitor/ios'
pod 'CapacitorApp', :path => '../../../../node_modules/.pnpm/@capacitor+app@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/app' pod 'CapacitorCommunityScreenBrightness', :path => '../../../../node_modules/.pnpm/@capacitor-community+screen-brightness@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor-community/screen-brightness'
pod 'CapacitorBrowser', :path => '../../../../node_modules/.pnpm/@capacitor+browser@5.1.0_@capacitor+core@5.5.0/node_modules/@capacitor/browser' pod 'CapacitorApp', :path => '../../../../node_modules/.pnpm/@capacitor+app@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/app'
pod 'CapacitorClipboard', :path => '../../../../node_modules/.pnpm/@capacitor+clipboard@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/clipboard' pod 'CapacitorBrowser', :path => '../../../../node_modules/.pnpm/@capacitor+browser@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/browser'
pod 'CapacitorDevice', :path => '../../../../node_modules/.pnpm/@capacitor+device@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/device' pod 'CapacitorClipboard', :path => '../../../../node_modules/.pnpm/@capacitor+clipboard@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/clipboard'
pod 'CapacitorDialog', :path => '../../../../node_modules/.pnpm/@capacitor+dialog@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/dialog' pod 'CapacitorDevice', :path => '../../../../node_modules/.pnpm/@capacitor+device@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/device'
pod 'CapacitorFilesystem', :path => '../../../../node_modules/.pnpm/@capacitor+filesystem@5.1.4_@capacitor+core@5.5.0/node_modules/@capacitor/filesystem' pod 'CapacitorDialog', :path => '../../../../node_modules/.pnpm/@capacitor+dialog@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/dialog'
pod 'CapacitorGeolocation', :path => '../../../../node_modules/.pnpm/@capacitor+geolocation@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/geolocation' pod 'CapacitorFilesystem', :path => '../../../../node_modules/.pnpm/@capacitor+filesystem@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/filesystem'
pod 'CapacitorHaptics', :path => '../../../../node_modules/.pnpm/@capacitor+haptics@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/haptics' pod 'CapacitorGeolocation', :path => '../../../../node_modules/.pnpm/@capacitor+geolocation@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/geolocation'
pod 'CapacitorKeyboard', :path => '../../../../node_modules/.pnpm/@capacitor+keyboard@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/keyboard' pod 'CapacitorHaptics', :path => '../../../../node_modules/.pnpm/@capacitor+haptics@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/haptics'
pod 'CapacitorLocalNotifications', :path => '../../../../node_modules/.pnpm/@capacitor+local-notifications@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/local-notifications' pod 'CapacitorKeyboard', :path => '../../../../node_modules/.pnpm/@capacitor+keyboard@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/keyboard'
pod 'CapacitorNetwork', :path => '../../../../node_modules/.pnpm/@capacitor+network@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/network' pod 'CapacitorLocalNotifications', :path => '../../../../node_modules/.pnpm/@capacitor+local-notifications@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/local-notifications'
pod 'CapacitorPreferences', :path => '../../../../node_modules/.pnpm/@capacitor+preferences@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/preferences' pod 'CapacitorNetwork', :path => '../../../../node_modules/.pnpm/@capacitor+network@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/network'
pod 'CapacitorShare', :path => '../../../../node_modules/.pnpm/@capacitor+share@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/share' pod 'CapacitorPreferences', :path => '../../../../node_modules/.pnpm/@capacitor+preferences@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/preferences'
pod 'CapacitorSplashScreen', :path => '../../../../node_modules/.pnpm/@capacitor+splash-screen@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/splash-screen' pod 'CapacitorScreenOrientation', :path => '../../../../node_modules/.pnpm/@capacitor+screen-orientation@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/screen-orientation'
pod 'TransistorsoftCapacitorBackgroundFetch', :path => '../../../../node_modules/.pnpm/@transistorsoft+capacitor-background-fetch@5.1.1_@capacitor+core@5.5.0/node_modules/@transistorsoft/capacitor-background-fetch' pod 'CapacitorShare', :path => '../../../../node_modules/.pnpm/@capacitor+share@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/share'
pod 'CapacitorSecureStoragePlugin', :path => '../../../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@5.5.0/node_modules/capacitor-secure-storage-plugin' pod 'CapacitorSplashScreen', :path => '../../../../node_modules/.pnpm/@capacitor+splash-screen@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/splash-screen'
pod 'TransistorsoftCapacitorBackgroundFetch', :path => '../../../../node_modules/.pnpm/@transistorsoft+capacitor-background-fetch@5.2.0_@capacitor+core@6.1.1/node_modules/@transistorsoft/capacitor-background-fetch'
pod 'CapacitorSecureStoragePlugin', :path => '../../../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@6.1.1/node_modules/capacitor-secure-storage-plugin'
pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins' pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'
end end

View File

@@ -1,64 +1,70 @@
PODS: PODS:
- Capacitor (5.5.0): - Capacitor (6.1.1):
- CapacitorCordova - CapacitorCordova
- CapacitorApp (5.0.6): - CapacitorApp (6.0.0):
- Capacitor - Capacitor
- CapacitorBrowser (5.1.0): - CapacitorBrowser (6.0.1):
- Capacitor - Capacitor
- CapacitorClipboard (5.0.6): - CapacitorClipboard (6.0.0):
- Capacitor - Capacitor
- CapacitorCordova (5.5.0) - CapacitorCommunityScreenBrightness (6.0.0):
- CapacitorDevice (5.0.6):
- Capacitor - Capacitor
- CapacitorDialog (5.0.6): - CapacitorCordova (6.1.1)
- CapacitorDevice (6.0.0):
- Capacitor - Capacitor
- CapacitorFilesystem (5.1.4): - CapacitorDialog (6.0.0):
- Capacitor - Capacitor
- CapacitorGeolocation (5.0.6): - CapacitorFilesystem (6.0.0):
- Capacitor - Capacitor
- CapacitorHaptics (5.0.6): - CapacitorGeolocation (6.0.0):
- Capacitor - Capacitor
- CapacitorKeyboard (5.0.6): - CapacitorHaptics (6.0.0):
- Capacitor - Capacitor
- CapacitorLocalNotifications (5.0.6): - CapacitorKeyboard (6.0.1):
- Capacitor - Capacitor
- CapacitorNetwork (5.0.6): - CapacitorLocalNotifications (6.0.0):
- Capacitor - Capacitor
- CapacitorPreferences (5.0.6): - CapacitorNetwork (6.0.1):
- Capacitor
- CapacitorPreferences (6.0.1):
- Capacitor
- CapacitorScreenOrientation (6.0.1):
- Capacitor - Capacitor
- CapacitorSecureStoragePlugin (0.9.0): - CapacitorSecureStoragePlugin (0.9.0):
- Capacitor - Capacitor
- SwiftKeychainWrapper - SwiftKeychainWrapper
- CapacitorShare (5.0.6): - CapacitorShare (6.0.1):
- Capacitor - Capacitor
- CapacitorSplashScreen (5.0.6): - CapacitorSplashScreen (6.0.1):
- Capacitor - Capacitor
- CordovaPlugins (5.5.0): - CordovaPlugins (6.1.1):
- CapacitorCordova - CapacitorCordova
- SwiftKeychainWrapper (4.0.1) - SwiftKeychainWrapper (4.0.1)
- TransistorsoftCapacitorBackgroundFetch (5.1.1): - TransistorsoftCapacitorBackgroundFetch (5.2.0):
- Capacitor - Capacitor
DEPENDENCIES: DEPENDENCIES:
- "Capacitor (from `../../../../node_modules/.pnpm/@capacitor+ios@5.5.0_@capacitor+core@5.5.0/node_modules/@capacitor/ios`)" - "Capacitor (from `../../../../node_modules/.pnpm/@capacitor+ios@6.1.1_@capacitor+core@6.1.1/node_modules/@capacitor/ios`)"
- "CapacitorApp (from `../../../../node_modules/.pnpm/@capacitor+app@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/app`)" - "CapacitorApp (from `../../../../node_modules/.pnpm/@capacitor+app@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/app`)"
- "CapacitorBrowser (from `../../../../node_modules/.pnpm/@capacitor+browser@5.1.0_@capacitor+core@5.5.0/node_modules/@capacitor/browser`)" - "CapacitorBrowser (from `../../../../node_modules/.pnpm/@capacitor+browser@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/browser`)"
- "CapacitorClipboard (from `../../../../node_modules/.pnpm/@capacitor+clipboard@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/clipboard`)" - "CapacitorClipboard (from `../../../../node_modules/.pnpm/@capacitor+clipboard@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/clipboard`)"
- "CapacitorCordova (from `../../../../node_modules/.pnpm/@capacitor+ios@5.5.0_@capacitor+core@5.5.0/node_modules/@capacitor/ios`)" - "CapacitorCommunityScreenBrightness (from `../../../../node_modules/.pnpm/@capacitor-community+screen-brightness@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor-community/screen-brightness`)"
- "CapacitorDevice (from `../../../../node_modules/.pnpm/@capacitor+device@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/device`)" - "CapacitorCordova (from `../../../../node_modules/.pnpm/@capacitor+ios@6.1.1_@capacitor+core@6.1.1/node_modules/@capacitor/ios`)"
- "CapacitorDialog (from `../../../../node_modules/.pnpm/@capacitor+dialog@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/dialog`)" - "CapacitorDevice (from `../../../../node_modules/.pnpm/@capacitor+device@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/device`)"
- "CapacitorFilesystem (from `../../../../node_modules/.pnpm/@capacitor+filesystem@5.1.4_@capacitor+core@5.5.0/node_modules/@capacitor/filesystem`)" - "CapacitorDialog (from `../../../../node_modules/.pnpm/@capacitor+dialog@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/dialog`)"
- "CapacitorGeolocation (from `../../../../node_modules/.pnpm/@capacitor+geolocation@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/geolocation`)" - "CapacitorFilesystem (from `../../../../node_modules/.pnpm/@capacitor+filesystem@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/filesystem`)"
- "CapacitorHaptics (from `../../../../node_modules/.pnpm/@capacitor+haptics@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/haptics`)" - "CapacitorGeolocation (from `../../../../node_modules/.pnpm/@capacitor+geolocation@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/geolocation`)"
- "CapacitorKeyboard (from `../../../../node_modules/.pnpm/@capacitor+keyboard@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/keyboard`)" - "CapacitorHaptics (from `../../../../node_modules/.pnpm/@capacitor+haptics@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/haptics`)"
- "CapacitorLocalNotifications (from `../../../../node_modules/.pnpm/@capacitor+local-notifications@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/local-notifications`)" - "CapacitorKeyboard (from `../../../../node_modules/.pnpm/@capacitor+keyboard@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/keyboard`)"
- "CapacitorNetwork (from `../../../../node_modules/.pnpm/@capacitor+network@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/network`)" - "CapacitorLocalNotifications (from `../../../../node_modules/.pnpm/@capacitor+local-notifications@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/local-notifications`)"
- "CapacitorPreferences (from `../../../../node_modules/.pnpm/@capacitor+preferences@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/preferences`)" - "CapacitorNetwork (from `../../../../node_modules/.pnpm/@capacitor+network@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/network`)"
- "CapacitorSecureStoragePlugin (from `../../../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@5.5.0/node_modules/capacitor-secure-storage-plugin`)" - "CapacitorPreferences (from `../../../../node_modules/.pnpm/@capacitor+preferences@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/preferences`)"
- "CapacitorShare (from `../../../../node_modules/.pnpm/@capacitor+share@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/share`)" - "CapacitorScreenOrientation (from `../../../../node_modules/.pnpm/@capacitor+screen-orientation@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/screen-orientation`)"
- "CapacitorSplashScreen (from `../../../../node_modules/.pnpm/@capacitor+splash-screen@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/splash-screen`)" - "CapacitorSecureStoragePlugin (from `../../../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@6.1.1/node_modules/capacitor-secure-storage-plugin`)"
- "CapacitorShare (from `../../../../node_modules/.pnpm/@capacitor+share@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/share`)"
- "CapacitorSplashScreen (from `../../../../node_modules/.pnpm/@capacitor+splash-screen@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/splash-screen`)"
- CordovaPlugins (from `../capacitor-cordova-ios-plugins`) - CordovaPlugins (from `../capacitor-cordova-ios-plugins`)
- "TransistorsoftCapacitorBackgroundFetch (from `../../../../node_modules/.pnpm/@transistorsoft+capacitor-background-fetch@5.1.1_@capacitor+core@5.5.0/node_modules/@transistorsoft/capacitor-background-fetch`)" - "TransistorsoftCapacitorBackgroundFetch (from `../../../../node_modules/.pnpm/@transistorsoft+capacitor-background-fetch@5.2.0_@capacitor+core@6.1.1/node_modules/@transistorsoft/capacitor-background-fetch`)"
SPEC REPOS: SPEC REPOS:
trunk: trunk:
@@ -66,66 +72,72 @@ SPEC REPOS:
EXTERNAL SOURCES: EXTERNAL SOURCES:
Capacitor: Capacitor:
:path: "../../../../node_modules/.pnpm/@capacitor+ios@5.5.0_@capacitor+core@5.5.0/node_modules/@capacitor/ios" :path: "../../../../node_modules/.pnpm/@capacitor+ios@6.1.1_@capacitor+core@6.1.1/node_modules/@capacitor/ios"
CapacitorApp: CapacitorApp:
:path: "../../../../node_modules/.pnpm/@capacitor+app@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/app" :path: "../../../../node_modules/.pnpm/@capacitor+app@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/app"
CapacitorBrowser: CapacitorBrowser:
:path: "../../../../node_modules/.pnpm/@capacitor+browser@5.1.0_@capacitor+core@5.5.0/node_modules/@capacitor/browser" :path: "../../../../node_modules/.pnpm/@capacitor+browser@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/browser"
CapacitorClipboard: CapacitorClipboard:
:path: "../../../../node_modules/.pnpm/@capacitor+clipboard@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/clipboard" :path: "../../../../node_modules/.pnpm/@capacitor+clipboard@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/clipboard"
CapacitorCommunityScreenBrightness:
:path: "../../../../node_modules/.pnpm/@capacitor-community+screen-brightness@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor-community/screen-brightness"
CapacitorCordova: CapacitorCordova:
:path: "../../../../node_modules/.pnpm/@capacitor+ios@5.5.0_@capacitor+core@5.5.0/node_modules/@capacitor/ios" :path: "../../../../node_modules/.pnpm/@capacitor+ios@6.1.1_@capacitor+core@6.1.1/node_modules/@capacitor/ios"
CapacitorDevice: CapacitorDevice:
:path: "../../../../node_modules/.pnpm/@capacitor+device@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/device" :path: "../../../../node_modules/.pnpm/@capacitor+device@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/device"
CapacitorDialog: CapacitorDialog:
:path: "../../../../node_modules/.pnpm/@capacitor+dialog@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/dialog" :path: "../../../../node_modules/.pnpm/@capacitor+dialog@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/dialog"
CapacitorFilesystem: CapacitorFilesystem:
:path: "../../../../node_modules/.pnpm/@capacitor+filesystem@5.1.4_@capacitor+core@5.5.0/node_modules/@capacitor/filesystem" :path: "../../../../node_modules/.pnpm/@capacitor+filesystem@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/filesystem"
CapacitorGeolocation: CapacitorGeolocation:
:path: "../../../../node_modules/.pnpm/@capacitor+geolocation@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/geolocation" :path: "../../../../node_modules/.pnpm/@capacitor+geolocation@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/geolocation"
CapacitorHaptics: CapacitorHaptics:
:path: "../../../../node_modules/.pnpm/@capacitor+haptics@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/haptics" :path: "../../../../node_modules/.pnpm/@capacitor+haptics@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/haptics"
CapacitorKeyboard: CapacitorKeyboard:
:path: "../../../../node_modules/.pnpm/@capacitor+keyboard@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/keyboard" :path: "../../../../node_modules/.pnpm/@capacitor+keyboard@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/keyboard"
CapacitorLocalNotifications: CapacitorLocalNotifications:
:path: "../../../../node_modules/.pnpm/@capacitor+local-notifications@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/local-notifications" :path: "../../../../node_modules/.pnpm/@capacitor+local-notifications@6.0.0_@capacitor+core@6.1.1/node_modules/@capacitor/local-notifications"
CapacitorNetwork: CapacitorNetwork:
:path: "../../../../node_modules/.pnpm/@capacitor+network@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/network" :path: "../../../../node_modules/.pnpm/@capacitor+network@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/network"
CapacitorPreferences: CapacitorPreferences:
:path: "../../../../node_modules/.pnpm/@capacitor+preferences@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/preferences" :path: "../../../../node_modules/.pnpm/@capacitor+preferences@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/preferences"
CapacitorScreenOrientation:
:path: "../../../../node_modules/.pnpm/@capacitor+screen-orientation@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/screen-orientation"
CapacitorSecureStoragePlugin: CapacitorSecureStoragePlugin:
:path: "../../../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@5.5.0/node_modules/capacitor-secure-storage-plugin" :path: "../../../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@6.1.1/node_modules/capacitor-secure-storage-plugin"
CapacitorShare: CapacitorShare:
:path: "../../../../node_modules/.pnpm/@capacitor+share@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/share" :path: "../../../../node_modules/.pnpm/@capacitor+share@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/share"
CapacitorSplashScreen: CapacitorSplashScreen:
:path: "../../../../node_modules/.pnpm/@capacitor+splash-screen@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/splash-screen" :path: "../../../../node_modules/.pnpm/@capacitor+splash-screen@6.0.1_@capacitor+core@6.1.1/node_modules/@capacitor/splash-screen"
CordovaPlugins: CordovaPlugins:
:path: "../capacitor-cordova-ios-plugins" :path: "../capacitor-cordova-ios-plugins"
TransistorsoftCapacitorBackgroundFetch: TransistorsoftCapacitorBackgroundFetch:
:path: "../../../../node_modules/.pnpm/@transistorsoft+capacitor-background-fetch@5.1.1_@capacitor+core@5.5.0/node_modules/@transistorsoft/capacitor-background-fetch" :path: "../../../../node_modules/.pnpm/@transistorsoft+capacitor-background-fetch@5.2.0_@capacitor+core@6.1.1/node_modules/@transistorsoft/capacitor-background-fetch"
SPEC CHECKSUMS: SPEC CHECKSUMS:
Capacitor: 57890b363df14d5d2d5d8461aa23e886cb34da2a Capacitor: 8941aba4364ba9d1b22188569001f2ce45cc2b00
CapacitorApp: 024e1b1bea5f883d79f6330d309bc441c88ad04a CapacitorApp: 9d53aec7101f7b030a950c5bdc4df8612576b279
CapacitorBrowser: 7a0fb6a1011abfaaf2dfedfd8248f942a8eda3d6 CapacitorBrowser: 473c7fd70ddbe541608ff09ec1be14da0078279e
CapacitorClipboard: 77edf49827ea21da2a9c05c690a4a6a4d07199c4 CapacitorClipboard: 80282f684154124b9019ebf401235b70b0cf4994
CapacitorCordova: 3d3908a3d208a11a75f9df3b18c4405c4de76e1d CapacitorCommunityScreenBrightness: 250184917bd0738a681c026a9513e18ddb0cb49b
CapacitorDevice: 2c968f98a1ec4d22357418c1521e7ddc46c675e6 CapacitorCordova: 8f2cc8d8d3619c566e9418fe8772064a94266106
CapacitorDialog: 0f3c15dfe9414b83bc64aef4078f1b92bcfead26 CapacitorDevice: f8fd88f9edd1261c55a109f32015b09bbbfdc4a0
CapacitorFilesystem: af704badfbc69f6f8623d9ed313e5490e3723dcb CapacitorDialog: 64aa82840ee5e9c066f9e1a49c1e186afd3f24d2
CapacitorGeolocation: 7be5f77abc205c0efe319fff8587a7183e7b0240 CapacitorFilesystem: 60e59ba274c234a979e7a3be2552feaadcee4263
CapacitorHaptics: 1fffc1217c7e64a472d7845be50fb0c2f7d4204c CapacitorGeolocation: 1f12bbe372b65116e851bd8e3a94cf0ef9bacebb
CapacitorKeyboard: b978154b024a5f65e044908e37d15b7de58b9d12 CapacitorHaptics: 9ebc9363f0e9b8eb4295088a0b474530acf1859b
CapacitorLocalNotifications: c2d8b14794064fd4814b1d6c4ddbac8029afa295 CapacitorKeyboard: 5f32a712adf41e07a61caafb82cf29fb6d8ba123
CapacitorNetwork: d80b3e79bef6ec37640ee2806c19771f07ff2d0c CapacitorLocalNotifications: 4ab68f0be5f697a579558fadd307d823a9ec1c26
CapacitorPreferences: f03954bcb0ff09c792909e46bff88e3183c16b10 CapacitorNetwork: 5c94acfdddc22043f2ffaff224ce9b4aa5a179f0
CapacitorPreferences: 72909b165bc7807103778ddbb86d5d8ce06abf71
CapacitorScreenOrientation: 3d4965dcbda5d901b6a48350a5d270ad7d404e05
CapacitorSecureStoragePlugin: e91d7df060f2495a1acff9583641a6953e3aacba CapacitorSecureStoragePlugin: e91d7df060f2495a1acff9583641a6953e3aacba
CapacitorShare: cd41743331cb71d217c029de54b681cbd91e0fcc CapacitorShare: 02222f2457ff003e642370a9c1ecd101baaa27c8
CapacitorSplashScreen: 5fa2ab5e46cf5cc530cf16a51c80c7a986579ccd CapacitorSplashScreen: 61645214e8f955ff2b80f16a6a3648960fe4c89f
CordovaPlugins: de5669381702d76ed5b1d442177a6a5fc3252a9d CordovaPlugins: 1078156cfc354dd440b38ce4062e69fd9b07033c
SwiftKeychainWrapper: 807ba1d63c33a7d0613288512399cd1eda1e470c SwiftKeychainWrapper: 807ba1d63c33a7d0613288512399cd1eda1e470c
TransistorsoftCapacitorBackgroundFetch: ce4b3e01b898cef516e68485d2160a078016ee97 TransistorsoftCapacitorBackgroundFetch: f130c5dcb6048401e74cc531e691e1d513045366
PODFILE CHECKSUM: 229278f2c257e8ab555325c7115b2e187e8e628d PODFILE CHECKSUM: d0561b89b66368df409c77587e3e02b48e737203
COCOAPODS: 1.13.0 COCOAPODS: 1.15.2

View File

@@ -1,7 +1,7 @@
{ {
"name": "@openstapps/app", "name": "@openstapps/app",
"description": "The generic app tailored to fulfill needs of German universities, written using Ionic Framework.", "description": "The generic app tailored to fulfill needs of German universities, written using Ionic Framework.",
"version": "3.2.0", "version": "3.3.4",
"private": true, "private": true,
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"author": "Karl-Philipp Wulfert <krlwlfrt@gmail.com>", "author": "Karl-Philipp Wulfert <krlwlfrt@gmail.com>",
@@ -61,21 +61,23 @@
"@angular/router": "17.3.0", "@angular/router": "17.3.0",
"@awesome-cordova-plugins/calendar": "6.6.0", "@awesome-cordova-plugins/calendar": "6.6.0",
"@awesome-cordova-plugins/core": "6.6.0", "@awesome-cordova-plugins/core": "6.6.0",
"@capacitor/app": "5.0.7", "@capacitor-community/screen-brightness": "6.0.0",
"@capacitor/browser": "5.2.0", "@capacitor/app": "6.0.0",
"@capacitor/clipboard": "5.0.7", "@capacitor/browser": "6.0.1",
"@capacitor/core": "5.7.3", "@capacitor/clipboard": "6.0.0",
"@capacitor/device": "5.0.7", "@capacitor/core": "6.1.1",
"@capacitor/dialog": "5.0.7", "@capacitor/device": "6.0.0",
"@capacitor/filesystem": "5.2.1", "@capacitor/dialog": "6.0.0",
"@capacitor/geolocation": "5.0.7", "@capacitor/filesystem": "6.0.0",
"@capacitor/haptics": "5.0.7", "@capacitor/geolocation": "6.0.0",
"@capacitor/keyboard": "5.0.8", "@capacitor/haptics": "6.0.0",
"@capacitor/local-notifications": "5.0.7", "@capacitor/keyboard": "6.0.1",
"@capacitor/network": "5.0.7", "@capacitor/local-notifications": "6.0.0",
"@capacitor/preferences": "5.0.7", "@capacitor/network": "6.0.1",
"@capacitor/share": "5.0.7", "@capacitor/preferences": "6.0.1",
"@capacitor/splash-screen": "5.0.7", "@capacitor/screen-orientation": "6.0.1",
"@capacitor/share": "6.0.1",
"@capacitor/splash-screen": "6.0.1",
"@ionic-native/core": "5.36.0", "@ionic-native/core": "5.36.0",
"@ionic/angular": "7.8.0", "@ionic/angular": "7.8.0",
"@ionic/storage-angular": "4.0.0", "@ionic/storage-angular": "4.0.0",
@@ -95,7 +97,7 @@
"form-data": "4.0.0", "form-data": "4.0.0",
"geojson": "0.5.0", "geojson": "0.5.0",
"ionic-appauth": "0.9.0", "ionic-appauth": "0.9.0",
"jsonpath-plus": "6.0.1", "jsonpath-plus": "10.0.6",
"maplibre-gl": "4.0.2", "maplibre-gl": "4.0.2",
"material-symbols": "0.17.1", "material-symbols": "0.17.1",
"moment": "2.30.1", "moment": "2.30.1",
@@ -127,10 +129,10 @@
"@angular/language-server": "17.3.0", "@angular/language-server": "17.3.0",
"@angular/language-service": "17.3.0", "@angular/language-service": "17.3.0",
"@angular/platform-browser-dynamic": "17.3.0", "@angular/platform-browser-dynamic": "17.3.0",
"@capacitor/android": "5.7.3", "@capacitor/android": "6.1.1",
"@capacitor/assets": "3.0.4", "@capacitor/assets": "3.0.4",
"@capacitor/cli": "5.7.3", "@capacitor/cli": "6.1.1",
"@capacitor/ios": "5.7.3", "@capacitor/ios": "6.1.1",
"@compodoc/compodoc": "1.1.23", "@compodoc/compodoc": "1.1.23",
"@cypress/schematic": "2.5.1", "@cypress/schematic": "2.5.1",
"@ionic/angular-toolkit": "11.0.1", "@ionic/angular-toolkit": "11.0.1",

View File

@@ -0,0 +1,8 @@
import {networkInterfaces} from 'os';
console.log(
Object.entries(networkInterfaces())
.map(([, info]) => info)
.flat()
.find(info => info && !info.internal && info.family === 'IPv4')?.address,
);

View File

@@ -13,11 +13,15 @@
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {Component} from '@angular/core'; import {ChangeDetectionStrategy, Component, inject} from '@angular/core';
import {ConfigProvider} from '../config/config.provider';
@Component({ @Component({
selector: 'about-changelog', selector: 'about-changelog',
templateUrl: 'about-changelog.html', templateUrl: 'about-changelog.html',
styleUrls: ['about-changelog.scss', './about-page/about-page.scss'], styleUrls: ['about-changelog.scss', './about-page/about-page.scss', 'release-notes-markdown.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class AboutChangelogComponent {} export class AboutChangelogComponent {
config = inject(ConfigProvider);
}

View File

@@ -16,14 +16,14 @@
<ion-header> <ion-header>
<ion-toolbar color="primary" mode="ios"> <ion-toolbar color="primary" mode="ios">
<ion-buttons slot="start"> <ion-buttons slot="start">
<ion-back-button></ion-back-button> <ion-back-button defaultHref="/about/changelog"></ion-back-button>
</ion-buttons> </ion-buttons>
<ion-title>Changelog</ion-title> <ion-title>{{ 'about.TECHNICAL_CHANGELOG' | translate }}</ion-title>
<!-- TODO: translation -->
</ion-toolbar> </ion-toolbar>
</ion-header> </ion-header>
<ion-content parallax> <ion-content parallax>
<p class="disclaimer">{{ 'about.TECHNICAL_CHANGELOG_DISCLAIMER' | translate }}</p>
<div class="about-changelog"> <div class="about-changelog">
<markdown src="assets/about/CHANGELOG.md"></markdown> <markdown class="release-notes" src="assets/CHANGELOG.md"></markdown>
</div> </div>
</ion-content> </ion-content>

View File

@@ -13,6 +13,13 @@
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
ion-content { ion-toggle {
--padding-start: 16px; width: fit-content;
padding-inline: var(--spacing-md);
color: var(--ion-color-primary-contrast);
}
.disclaimer {
margin-block: var(--spacing-xs);
color: var(--ion-color-primary-contrast);
} }

View File

@@ -12,24 +12,6 @@
* You should have received a copy of the GNU General Public License along with * You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
ion-content > div {
height: 100%;
}
cdk-virtual-scroll-viewport {
width: 100%;
height: 100%;
}
:host ::ng-deep {
.cdk-virtual-scroll-content-wrapper {
width: 100%;
}
}
.virtual-scroll-expander {
clear: both;
}
.supertext-icon { .supertext-icon {
height: 14px; height: 14px;

View File

@@ -0,0 +1,27 @@
/*
* 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 {ChangeDetectionStrategy, Component, inject} from '@angular/core';
import {ConfigProvider} from '../config/config.provider';
@Component({
selector: 'about-changelog',
templateUrl: 'about-release-notes.html',
styleUrls: ['about-release-notes.scss', './about-page/about-page.scss', 'release-notes-markdown.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AboutReleaseNotesComponent {
config = inject(ConfigProvider);
}

View File

@@ -0,0 +1,32 @@
<!--
~ 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/>.
-->
<ion-header>
<ion-toolbar color="primary" mode="ios">
<ion-buttons slot="start">
<ion-back-button defaultHref="/about"></ion-back-button>
</ion-buttons>
<ion-title>{{ 'about.CHANGELOG' | translate }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content parallax>
<ion-button fill="clear" [routerLink]="['/about', 'stapps-changelog']"
>{{ 'about.TECHNICAL_CHANGELOG' | translate
}}<ion-icon slot="end" name="arrow_forward" ios="arrow_forward_ios"></ion-icon
></ion-button>
@for (version of config.config.app.versionHistory; track version) {
<markdown class="release-notes" [data]="'releaseNotes' | translateSimple: version"></markdown>
}
</ion-content>

View File

@@ -0,0 +1,31 @@
/*!
* Copyright (C) 2021 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 '../../../theme/util/mixins';
ion-button {
--color: var(--ion-color-primary-contrast);
width: fit-content;
margin-block: 0;
}
.release-notes {
@include border-radius-in-parallax(var(--border-radius-default));
display: block;
margin: var(--spacing-md);
margin-block-start: 0;
background: var(--ion-item-background);
}

View File

@@ -29,10 +29,12 @@ import {AboutLicenseModalComponent} from './about-license-modal.component';
import {AboutChangelogComponent} from './about-changelog.component'; import {AboutChangelogComponent} from './about-changelog.component';
import {UtilModule} from '../../util/util.module'; import {UtilModule} from '../../util/util.module';
import {IonIconModule} from '../../util/ion-icon/ion-icon.module'; import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
import {AboutReleaseNotesComponent} from './about-release-notes.component';
const settingsRoutes: Routes = [ const settingsRoutes: Routes = [
{path: 'about', component: AboutPageComponent}, {path: 'about', component: AboutPageComponent},
{path: 'about/changelog', component: AboutChangelogComponent}, {path: 'about/changelog', component: AboutReleaseNotesComponent},
{path: 'about/stapps-changelog', component: AboutChangelogComponent},
{path: 'about/imprint', component: AboutPageComponent}, {path: 'about/imprint', component: AboutPageComponent},
{path: 'about/privacy', component: AboutPageComponent}, {path: 'about/privacy', component: AboutPageComponent},
{path: 'about/terms', component: AboutPageComponent}, {path: 'about/terms', component: AboutPageComponent},
@@ -49,6 +51,7 @@ const settingsRoutes: Routes = [
AboutLicensesComponent, AboutLicensesComponent,
AboutLicenseModalComponent, AboutLicenseModalComponent,
AboutChangelogComponent, AboutChangelogComponent,
AboutReleaseNotesComponent,
], ],
imports: [ imports: [
CommonModule, CommonModule,

View File

@@ -2,11 +2,11 @@ import {Injectable} from '@angular/core';
import {StorageProvider} from '../storage/storage.provider'; import {StorageProvider} from '../storage/storage.provider';
import {ConfigProvider} from '../config/config.provider'; import {ConfigProvider} from '../config/config.provider';
import {ModalController} from '@ionic/angular'; import {ModalController} from '@ionic/angular';
import {Capacitor} from '@capacitor/core';
import {ReleaseNotesComponent} from './release-notes.component'; import {ReleaseNotesComponent} from './release-notes.component';
import {SCAppVersionInfo} from '@openstapps/core'; import {SCAppVersionInfo} from '@openstapps/core';
import {App} from '@capacitor/app'; import {App} from '@capacitor/app';
import {coerce} from 'semver'; import {coerce} from 'semver';
import {Capacitor} from '@capacitor/core';
export const RELEASE_NOTES_SHOWN_KEY = 'release_notes_shown'; export const RELEASE_NOTES_SHOWN_KEY = 'release_notes_shown';
@@ -25,12 +25,11 @@ export class AppVersionService {
if (Capacitor.getPlatform() === 'web') { if (Capacitor.getPlatform() === 'web') {
return; return;
} }
const storedVersion = coerce(
(await this.storage.has(RELEASE_NOTES_SHOWN_KEY))
? await this.storage.get<string>(RELEASE_NOTES_SHOWN_KEY)
: '0.0.0',
)!;
const currentVersion = coerce(await App.getInfo().then(info => info.version))!; const currentVersion = coerce(await App.getInfo().then(info => info.version))!;
if (!(await this.storage.has(RELEASE_NOTES_SHOWN_KEY))) {
await this.storage.put(RELEASE_NOTES_SHOWN_KEY, currentVersion);
}
const storedVersion = coerce(await this.storage.get<string>(RELEASE_NOTES_SHOWN_KEY))!;
return this.config.config.app.versionHistory return this.config.config.app.versionHistory
?.filter(({version}) => { ?.filter(({version}) => {
@@ -39,7 +38,7 @@ export class AppVersionService {
const isNotFutureVersion = semanticVersion.compare(currentVersion) <= 0; const isNotFutureVersion = semanticVersion.compare(currentVersion) <= 0;
return wasNotShown && isNotFutureVersion; return wasNotShown && isNotFutureVersion;
}) })
?.sort((a, b) => coerce(a.version)!.compare(b.version)); ?.sort((a, b) => -coerce(a.version)!.compare(b.version));
} }
/** /**

View File

@@ -0,0 +1,26 @@
.release-notes {
max-width: 16cm;
}
.release-notes ::ng-deep {
h1 {
font-size: 1.5em;
font-weight: bold;
color: var(--ion-color-primary);
}
h2 {
font-size: 1.2em;
font-weight: bold;
}
h3 {
font-size: 1.1em;
font-weight: bold;
}
blockquote {
background: var(--ion-item-background);
border-left: 0.25em solid var(--ion-color-medium);
}
}

View File

@@ -9,7 +9,7 @@ import {UtilModule} from '../../util/util.module';
@Component({ @Component({
selector: 'stapps-release-notes', selector: 'stapps-release-notes',
templateUrl: 'release-notes.html', templateUrl: 'release-notes.html',
styleUrls: ['release-notes.scss'], styleUrls: ['release-notes.scss', 'release-notes-markdown.scss'],
standalone: true, standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
imports: [UtilModule, MarkdownModule, ThingTranslateModule, IonicModule, TranslateModule], imports: [UtilModule, MarkdownModule, ThingTranslateModule, IonicModule, TranslateModule],

View File

@@ -13,7 +13,7 @@
<ion-content parallax> <ion-content parallax>
@for (versionInfo of versionInfos; track versionInfo) { @for (versionInfo of versionInfos; track versionInfo) {
<markdown <markdown
class="content-card ion-padding" class="content-card ion-padding release-notes"
[data]="'releaseNotes' | translateSimple: versionInfo" [data]="'releaseNotes' | translateSimple: versionInfo"
></markdown> ></markdown>
} }

View File

@@ -9,5 +9,6 @@ ion-title {
display: block; display: block;
margin: var(--spacing-md); margin: var(--spacing-md);
margin-block-start: 0;
background: var(--ion-item-background); background: var(--ion-item-background);
} }

View File

@@ -81,7 +81,6 @@ export class AuthHelperService {
user[key as keyof SCUserConfiguration] = JSONPath({ 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, json: userInfo,
preventEval: true,
})[0]; })[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) {

View File

@@ -86,6 +86,8 @@ export abstract class AuthService implements IAuthService {
private _authenticatedSubject = new BehaviorSubject<boolean>(false); private _authenticatedSubject = new BehaviorSubject<boolean>(false);
private _loggedInSubject = new BehaviorSubject<boolean>(false);
private _initComplete = new BehaviorSubject<boolean>(false); private _initComplete = new BehaviorSubject<boolean>(false);
protected tokenHandler: TokenRequestHandler; protected tokenHandler: TokenRequestHandler;
@@ -133,6 +135,13 @@ export abstract class AuthService implements IAuthService {
return this._authenticatedSubject.asObservable(); return this._authenticatedSubject.asObservable();
} }
/**
* Similar to isAuthenticated$, but will also return true if the token is expired
*/
get isLoggedIn$(): Observable<boolean> {
return this._loggedInSubject.asObservable();
}
get initComplete$(): Observable<boolean> { get initComplete$(): Observable<boolean> {
return this._initComplete.asObservable(); return this._initComplete.asObservable();
} }
@@ -183,19 +192,20 @@ export abstract class AuthService implements IAuthService {
protected notifyActionListers(action: IAuthAction) { protected notifyActionListers(action: IAuthAction) {
/* eslint-disable unicorn/no-useless-undefined */ /* eslint-disable unicorn/no-useless-undefined */
switch (action.action) { switch (action.action) {
case AuthActions.RefreshFailed:
case AuthActions.SignInFailed: case AuthActions.SignInFailed:
case AuthActions.SignOutSuccess: case AuthActions.SignOutSuccess:
case AuthActions.SignOutFailed: { case AuthActions.SignOutFailed: {
this._tokenSubject.next(undefined); this._tokenSubject.next(undefined);
this._userSubject.next(undefined); this._userSubject.next(undefined);
this._authenticatedSubject.next(false); this._authenticatedSubject.next(false);
this._loggedInSubject.next(false);
break; break;
} }
case AuthActions.LoadTokenFromStorageFailed: { case AuthActions.LoadTokenFromStorageFailed: {
this._tokenSubject.next(undefined); this._tokenSubject.next(undefined);
this._userSubject.next(undefined); this._userSubject.next(undefined);
this._authenticatedSubject.next(false); this._authenticatedSubject.next(false);
this._loggedInSubject.next(false);
this._initComplete.next(true); this._initComplete.next(true);
break; break;
} }
@@ -203,11 +213,13 @@ export abstract class AuthService implements IAuthService {
case AuthActions.RefreshSuccess: { case AuthActions.RefreshSuccess: {
this._tokenSubject.next(action.tokenResponse); this._tokenSubject.next(action.tokenResponse);
this._authenticatedSubject.next(true); this._authenticatedSubject.next(true);
this._loggedInSubject.next(true);
break; break;
} }
case AuthActions.LoadTokenFromStorageSuccess: { case AuthActions.LoadTokenFromStorageSuccess: {
this._tokenSubject.next(action.tokenResponse); this._tokenSubject.next(action.tokenResponse);
this._authenticatedSubject.next((action.tokenResponse as TokenResponse).isValid(0)); this._authenticatedSubject.next((action.tokenResponse as TokenResponse).isValid(0));
this._loggedInSubject.next(true);
this._initComplete.next(true); this._initComplete.next(true);
break; break;
} }
@@ -442,7 +454,6 @@ export abstract class AuthService implements IAuthService {
public async refreshToken() { public async refreshToken() {
await this.requestTokenRefresh().catch(error => { await this.requestTokenRefresh().catch(error => {
this.storage.removeItem(TOKEN_RESPONSE_KEY);
this.notifyActionListers(AuthActionBuilder.RefreshFailed(error)); this.notifyActionListers(AuthActionBuilder.RefreshFailed(error));
}); });
} }

View File

@@ -81,6 +81,8 @@ export class PAIAAuthService {
private _authenticatedSubject = new BehaviorSubject<boolean>(false); private _authenticatedSubject = new BehaviorSubject<boolean>(false);
private _loggedIn = new BehaviorSubject<boolean>(false);
private _initComplete = new BehaviorSubject<boolean>(false); private _initComplete = new BehaviorSubject<boolean>(false);
protected tokenHandler: PAIATokenRequestHandler; protected tokenHandler: PAIATokenRequestHandler;
@@ -118,6 +120,13 @@ export class PAIAAuthService {
return this._authenticatedSubject.asObservable(); return this._authenticatedSubject.asObservable();
} }
/**
* Similar to isAuthenticated$, but will also return true if the token is expired
*/
get isLoggedIn$(): Observable<boolean> {
return this._loggedIn.asObservable();
}
get initComplete$(): Observable<boolean> { get initComplete$(): Observable<boolean> {
return this._initComplete.asObservable(); return this._initComplete.asObservable();
} }
@@ -170,23 +179,27 @@ export class PAIAAuthService {
this._tokenSubject.next(undefined); this._tokenSubject.next(undefined);
this._userSubject.next(undefined); this._userSubject.next(undefined);
this._authenticatedSubject.next(false); this._authenticatedSubject.next(false);
this._loggedIn.next(false);
break; break;
} }
case AuthActions.LoadTokenFromStorageFailed: { case AuthActions.LoadTokenFromStorageFailed: {
this._tokenSubject.next(undefined); this._tokenSubject.next(undefined);
this._userSubject.next(undefined); this._userSubject.next(undefined);
this._authenticatedSubject.next(false); this._authenticatedSubject.next(false);
this._loggedIn.next(false);
this._initComplete.next(true); this._initComplete.next(true);
break; break;
} }
case AuthActions.SignInSuccess: { case AuthActions.SignInSuccess: {
this._tokenSubject.next(action.tokenResponse); this._tokenSubject.next(action.tokenResponse);
this._authenticatedSubject.next(true); this._authenticatedSubject.next(true);
this._loggedIn.next(true);
break; break;
} }
case AuthActions.LoadTokenFromStorageSuccess: { case AuthActions.LoadTokenFromStorageSuccess: {
this._tokenSubject.next(action.tokenResponse); this._tokenSubject.next(action.tokenResponse);
this._authenticatedSubject.next((action.tokenResponse as TokenResponse).isValid(0)); this._authenticatedSubject.next((action.tokenResponse as TokenResponse).isValid(0));
this._loggedIn.next(true);
this._initComplete.next(true); this._initComplete.next(true);
break; break;
} }

View File

@@ -24,13 +24,17 @@ import {
} from './errors'; } from './errors';
import {NGXLogger} from 'ngx-logger'; import {NGXLogger} from 'ngx-logger';
import {sampleIndexResponse} from '../../_helpers/data/sample-configuration'; import {sampleIndexResponse} from '../../_helpers/data/sample-configuration';
import {BehaviorSubject} from 'rxjs';
import {InternetConnectionService} from '../../util/internet-connection.service';
describe('ConfigProvider', () => { describe('ConfigProvider', () => {
let internetConnectionServiceMock: {offline$: BehaviorSubject<boolean>};
let configProvider: ConfigProvider; let configProvider: ConfigProvider;
let storageProviderSpy: jasmine.SpyObj<StorageProvider>; let storageProviderSpy: jasmine.SpyObj<StorageProvider>;
let ngxLogger: jasmine.SpyObj<NGXLogger>; let ngxLogger: jasmine.SpyObj<NGXLogger>;
beforeEach(() => { beforeEach(() => {
internetConnectionServiceMock = {offline$: new BehaviorSubject<boolean>(false)};
storageProviderSpy = jasmine.createSpyObj('StorageProvider', ['init', 'get', 'has', 'put']); storageProviderSpy = jasmine.createSpyObj('StorageProvider', ['init', 'get', 'has', 'put']);
const webHttpClientMethodSpy = jasmine.createSpyObj('StAppsWebHttpClient', ['request']); const webHttpClientMethodSpy = jasmine.createSpyObj('StAppsWebHttpClient', ['request']);
ngxLogger = jasmine.createSpyObj('NGXLogger', ['log', 'error', 'warn']); ngxLogger = jasmine.createSpyObj('NGXLogger', ['log', 'error', 'warn']);
@@ -51,6 +55,10 @@ describe('ConfigProvider', () => {
provide: NGXLogger, provide: NGXLogger,
useValue: ngxLogger, useValue: ngxLogger,
}, },
{
provide: InternetConnectionService,
useValue: internetConnectionServiceMock,
},
], ],
}); });
@@ -59,7 +67,8 @@ describe('ConfigProvider', () => {
it('should fetch app configuration', async () => { 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(); await configProvider.fetch();
const result = configProvider.config;
expect(result).toEqual(sampleIndexResponse); expect(result).toEqual(sampleIndexResponse);
}); });
@@ -75,6 +84,22 @@ describe('ConfigProvider', () => {
expect(error).toEqual(new ConfigFetchError()); expect(error).toEqual(new ConfigFetchError());
}); });
it('should throw device offline error when offline', async () => {
// eslint-disable-next-line unicorn/error-message
let error = new Error('');
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
internetConnectionServiceMock.offline$ = new BehaviorSubject<boolean>(true);
try {
await configProvider.fetch();
} catch (error_) {
error = error_ as Error;
expect(error).toBeInstanceOf(ConfigFetchError);
expect(error.message).toContain('Device is offline.');
}
});
it('should init from remote and saved config not available', async () => { it('should init from remote and saved config not available', async () => {
storageProviderSpy.has.and.returnValue(Promise.resolve(false)); 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));
@@ -86,7 +111,7 @@ describe('ConfigProvider', () => {
expect(storageProviderSpy.has).toHaveBeenCalled(); expect(storageProviderSpy.has).toHaveBeenCalled();
expect(storageProviderSpy.get).toHaveBeenCalledTimes(0); expect(storageProviderSpy.get).toHaveBeenCalledTimes(0);
expect(configProvider.client.handshake).toHaveBeenCalled(); expect(configProvider.client.handshake).toHaveBeenCalled();
expect(await configProvider.getValue('name')).toEqual(sampleIndexResponse.app.name); expect(configProvider.getValue('name')).toEqual(sampleIndexResponse.app.name);
}); });
it('should throw error on failed initialisation', async () => { it('should throw error on failed initialisation', async () => {
@@ -108,6 +133,8 @@ describe('ConfigProvider', () => {
wrongConfig.backend.SCVersion = '0.1.0'; wrongConfig.backend.SCVersion = '0.1.0';
storageProviderSpy.get.and.returnValue(Promise.resolve(wrongConfig)); storageProviderSpy.get.and.returnValue(Promise.resolve(wrongConfig));
spyOn(configProvider.client, 'handshake').and.returnValue(Promise.resolve(sampleIndexResponse)); spyOn(configProvider.client, 'handshake').and.returnValue(Promise.resolve(sampleIndexResponse));
// sets StApps core version which has a different major version
configProvider.scVersion = '1.0.0';
await configProvider.init(); await configProvider.init();
expect(ngxLogger.warn).toHaveBeenCalledWith( expect(ngxLogger.warn).toHaveBeenCalledWith(
@@ -115,6 +142,19 @@ describe('ConfigProvider', () => {
); );
}); });
it('should load from storage if offline and same major version stored', async () => {
internetConnectionServiceMock = {offline$: new BehaviorSubject<boolean>(true)};
storageProviderSpy.has.and.returnValue(Promise.resolve(true));
const configInStorage = structuredClone(sampleIndexResponse);
configInStorage.backend.SCVersion = '1.0.0';
storageProviderSpy.get.and.returnValue(Promise.resolve(configInStorage));
// sets StApps core version which has the same major version
configProvider.scVersion = '1.1.1';
await configProvider.init();
expect(configProvider.getAnyValue('app')).toEqual(configInStorage.app);
});
it('should throw error on saved app configuration not available', async () => { it('should throw error on saved app configuration not available', async () => {
storageProviderSpy.has.and.returnValue(Promise.resolve(false)); storageProviderSpy.has.and.returnValue(Promise.resolve(false));
// eslint-disable-next-line unicorn/error-message // eslint-disable-next-line unicorn/error-message
@@ -153,4 +193,31 @@ describe('ConfigProvider', () => {
expect(configProvider.getValue('name')).toEqual(sampleIndexResponse.app.name); expect(configProvider.getValue('name')).toEqual(sampleIndexResponse.app.name);
}); });
it('should fetch new config from remote on init', async () => {
storageProviderSpy.has.and.returnValue(Promise.resolve(true));
storageProviderSpy.get.and.returnValue(Promise.resolve(sampleIndexResponse));
spyOn(configProvider, 'fetch');
await configProvider.init();
expect(configProvider.fetch).toHaveBeenCalled();
});
it('should update the local config with the one from remote', async () => {
storageProviderSpy.has.and.returnValue(Promise.resolve(true));
storageProviderSpy.get.and.returnValue(Promise.resolve(sampleIndexResponse));
const newConfig = structuredClone(sampleIndexResponse);
newConfig.app.name = 'New app name';
spyOn(configProvider.client, 'handshake').and.returnValue(Promise.resolve(newConfig));
await configProvider.init();
// Validate that the initial configuration is loaded
expect(configProvider.getValue('name')).toEqual(sampleIndexResponse.app.name);
// Fetch the new configuration from the remote
await configProvider.fetch();
// Validate that the new configuration is now set
expect(configProvider.getValue('name')).toEqual(newConfig.app.name);
});
}); });

View File

@@ -27,6 +27,8 @@ import {
SavedConfigNotAvailable, SavedConfigNotAvailable,
WrongConfigVersionInStorage, WrongConfigVersionInStorage,
} from './errors'; } from './errors';
import {InternetConnectionService} from '../../util/internet-connection.service';
import {firstValueFrom} from 'rxjs';
/** /**
* Key to store config in storage module * Key to store config in storage module
@@ -72,6 +74,7 @@ export class ConfigProvider {
private readonly storageProvider: StorageProvider, private readonly storageProvider: StorageProvider,
swHttpClient: StAppsWebHttpClient, swHttpClient: StAppsWebHttpClient,
private readonly logger: NGXLogger, private readonly logger: NGXLogger,
private readonly internetConnectionService: InternetConnectionService,
) { ) {
console.log('config init'); console.log('config init');
this.client = new Client(swHttpClient, environment.backend_url, environment.backend_version); this.client = new Client(swHttpClient, environment.backend_url, environment.backend_version);
@@ -80,11 +83,20 @@ export class ConfigProvider {
/** /**
* Fetches configuration from backend * Fetches configuration from backend
*/ */
async fetch(): Promise<SCIndexResponse> { async fetch(): Promise<void> {
try { try {
return await this.client.handshake(this.scVersion); const isOffline = await firstValueFrom(this.internetConnectionService.offline$);
} catch { if (isOffline) {
throw new ConfigFetchError(); throw new Error('Device is offline.');
} else {
const fetchedConfig: SCIndexResponse = await this.client.handshake(this.scVersion);
await this.set(fetchedConfig);
this.logger.log(`Configuration updated from remote`);
}
} catch (error) {
const error_ = error instanceof Error ? new ConfigFetchError(error.message) : new ConfigFetchError();
this.logger.warn(`Failed to fetch remote configuration:`, error_);
throw error_; // Rethrow the error to handle it in init()
} }
} }
@@ -112,40 +124,33 @@ export class ConfigProvider {
/** /**
* Initialises the ConfigProvider * Initialises the ConfigProvider
* @throws ConfigInitError if no configuration could be loaded. * @throws ConfigInitError if no configuration could be loaded both locally and remote.
* @throws WrongConfigVersionInStorage if fetch failed and saved config has wrong SCVersion
*/ */
async init(): Promise<void> { async init(): Promise<void> {
let loadError;
let fetchError;
// load saved configuration
try { try {
// Attempt to load the configuration from local storage
this.config = await this.loadLocal(); this.config = await this.loadLocal();
this.firstSession = false; this.firstSession = false;
this.logger.log(`initialised configuration from storage`); this.logger.log(`initialised configuration from storage`);
if (this.config.backend.SCVersion !== this.scVersion) {
loadError = new WrongConfigVersionInStorage(this.scVersion, this.config.backend.SCVersion); // Check if the stored configuration has the correct version
if (this.config.backend.SCVersion.split('.')[0] !== this.scVersion.split('.')[0]) {
throw new WrongConfigVersionInStorage(this.scVersion, this.config.backend.SCVersion);
} }
} catch (error) {
loadError = error; // Fetch the remote configuration in a non-blocking manner
} void this.fetch();
// fetch remote configuration from backend } catch (loadError) {
try {
const fetchedConfig: SCIndexResponse = await this.fetch();
await this.set(fetchedConfig);
this.logger.log(`initialised configuration from remote`);
} catch (error) {
fetchError = error;
}
// check for occurred errors and throw them
if (loadError !== undefined && fetchError !== undefined) {
throw new ConfigInitError();
}
if (loadError !== undefined) {
this.logger.warn(loadError); this.logger.warn(loadError);
}
if (fetchError !== undefined) { try {
this.logger.warn(fetchError); // If local loading fails, immediately try to fetch the configuration from remote
await this.fetch();
} catch (fetchError) {
this.logger.warn(`Failed to fetch remote configuration:`, fetchError);
// If both local loading and remote fetching fail, throw ConfigInitError
throw new ConfigInitError();
}
} }
} }

View File

@@ -19,8 +19,10 @@ import {AppError} from '../../_helpers/errors';
* Error that is thrown when fetching from backend fails * Error that is thrown when fetching from backend fails
*/ */
export class ConfigFetchError extends AppError { export class ConfigFetchError extends AppError {
constructor() { constructor(reason?: string) {
super('ConfigFetchError', 'App configuration could not be fetched!'); const defaultMessage = 'App configuration could not be fetched!';
const message = reason ? `${defaultMessage} ${reason}` : defaultMessage;
super('ConfigFetchError', message);
} }
} }

View File

@@ -33,9 +33,9 @@ ion-item {
margin: var(--spacing-sm); margin: var(--spacing-sm);
ion-thumbnail { ion-thumbnail {
--ion-margin: var(--spacing-xs); --size: 36px;
margin-block: auto; margin: 0;
margin-inline: var(--spacing-md); margin-inline: var(--spacing-md);
padding: 0; padding: 0;
} }

View File

@@ -80,13 +80,12 @@
</ion-item> </ion-item>
<ion-item> <ion-item>
<ion-checkbox <ion-checkbox
class="ion-text-wrap"
color="primary" color="primary"
label-placement="end" label-placement="end"
justify="start" justify="start"
[(ngModel)]="termsAgree" [(ngModel)]="termsAgree"
name="termsAgree" name="termsAgree"
>{{ 'feedback.form.termsAgree.0' | translate }}</ion-checkbox ><span class="ion-text-wrap">{{ 'feedback.form.termsAgree.0' | translate }}</span></ion-checkbox
> >
</ion-item> </ion-item>
<ion-item lines="none"> <ion-item lines="none">
@@ -104,7 +103,9 @@
justify="start" justify="start"
[(ngModel)]="protocolDataAgree" [(ngModel)]="protocolDataAgree"
name="protocolDataAgree" name="protocolDataAgree"
>{{ 'feedback.form.protocolDataAgree' | translate }}</ion-checkbox ><span class="ion-text-wrap">{{
'feedback.form.protocolDataAgree' | translate
}}</span></ion-checkbox
> >
</ion-item> </ion-item>
<ion-card> <ion-card>

View File

@@ -11,7 +11,7 @@ export class LibraryAccountPageComponent {
constructor(private readonly libraryAccountService: LibraryAccountService) {} constructor(private readonly libraryAccountService: LibraryAccountService) {}
async ionViewWillEnter(): Promise<void> { async ionViewWillEnter(): Promise<void> {
const patron = await this.libraryAccountService.getProfile(); const patron = await this.libraryAccountService.getPatron();
this.name = patron?.name; this.name = patron?.name;
} }
} }

View File

@@ -39,6 +39,8 @@ export class CheckedOutPageComponent {
async fetchItems() { async fetchItems() {
try { try {
// Prepare patron (status) for the items
await this.libraryAccountService.getPatron();
this.checkedOutItems = undefined; this.checkedOutItems = undefined;
this.checkedOutItems = await this.libraryAccountService.getFilteredItems([PAIADocumentStatus.Held]); this.checkedOutItems = await this.libraryAccountService.getFilteredItems([PAIADocumentStatus.Held]);
} catch { } catch {

View File

@@ -27,7 +27,7 @@
@for (checkedOutItem of checkedOutItems; track checkedOutItem) { @for (checkedOutItem of checkedOutItems; track checkedOutItem) {
<stapps-paia-item <stapps-paia-item
[item]="checkedOutItem" [item]="checkedOutItem"
[propertiesToShow]="['label', 'renewals', 'endtime']" [propertiesToShow]="['label', 'renewals', 'duedate']"
(documentAction)="onDocumentAction($event)" (documentAction)="onDocumentAction($event)"
listName="checked_out" listName="checked_out"
> >

View File

@@ -14,7 +14,8 @@
*/ */
import {Component, EventEmitter, Input, Output} from '@angular/core'; import {Component, EventEmitter, Input, Output} from '@angular/core';
import {DocumentAction, PAIADocument} from '../../../types'; import {DocumentAction, PAIADocument, PAIADocumentStatus, PAIADocumentVisualStatus} from '../../../types';
import {LibraryAccountService} from '../../library-account.service';
@Component({ @Component({
selector: 'stapps-paia-item', selector: 'stapps-paia-item',
@@ -22,7 +23,24 @@ import {DocumentAction, PAIADocument} from '../../../types';
styleUrls: ['./paiaitem.scss'], styleUrls: ['./paiaitem.scss'],
}) })
export class PAIAItemComponent { export class PAIAItemComponent {
@Input() item: PAIADocument; private _item: PAIADocument;
renewable: boolean;
visualStatus?: PAIADocumentVisualStatus;
constructor(private readonly libraryAccountService: LibraryAccountService) {}
@Input()
set item(value: PAIADocument) {
this._item = value;
void this.setRenewable();
this.visualStatus = this.getVisualStatus(Number(this.item.status));
}
get item(): PAIADocument {
return this._item;
}
@Input() @Input()
propertiesToShow: (keyof PAIADocument)[]; propertiesToShow: (keyof PAIADocument)[];
@@ -36,4 +54,23 @@ export class PAIAItemComponent {
async onClick(action: DocumentAction['action']) { async onClick(action: DocumentAction['action']) {
this.documentAction.emit({doc: this.item, action}); this.documentAction.emit({doc: this.item, action});
} }
private async setRenewable() {
const isActive = await this.libraryAccountService.isActivePatron();
this.renewable = isActive && Number(this.item.status) === PAIADocumentStatus.Held;
}
private getVisualStatus(status: PAIADocumentStatus): PAIADocumentVisualStatus | undefined {
switch (status) {
case PAIADocumentStatus.Ordered: {
return {color: 'warning', status: status, statusText: 'ordered'};
}
case PAIADocumentStatus.Provided: {
return {color: 'success', status: status, statusText: 'ready'};
}
default: {
return undefined;
}
}
}
} }

View File

@@ -14,16 +14,22 @@
--> -->
<ion-item> <ion-item>
<!-- TODO: text not selectable in Chrome, bugfix needed https://github.com/ionic-team/ionic-framework/issues/24956 -->
<ion-label class="ion-text-wrap"> <ion-label class="ion-text-wrap">
@if (item.about) { @if (item.about) {
<h2 class="name">{{ item.about }}</h2> <h2 class="name">
@if (visualStatus) {
<ion-badge [color]="visualStatus.color" slot="start">
{{ 'library.account.pages' + '.' + listName + '.' + visualStatus.statusText | translate }}
</ion-badge>
}
{{ item.about }}
</h2>
} }
@for (property of propertiesToShow; track property) { @for (property of propertiesToShow; track property) {
@if (item[property]) { @if (item[property]) {
<p> <p>
{{ 'library.account.pages' + '.' + listName + '.' + 'labels' + '.' + property | translate }}: {{ 'library.account.pages' + '.' + listName + '.' + 'labels' + '.' + property | translate }}:
@if (!['endtime', 'duedate'].includes(property)) { @if (!['starttime', 'duedate'].includes(property)) {
{{ item[property] }} {{ item[property] }}
} @else { } @else {
{{ $any(item[property]) | amDateFormat: 'll' }} {{ $any(item[property]) | amDateFormat: 'll' }}
@@ -40,7 +46,7 @@
<!-- >--> <!-- >-->
<!-- {{ 'library.account.actions.cancel.header' | translate }}</ion-button--> <!-- {{ 'library.account.actions.cancel.header' | translate }}</ion-button-->
<!-- >--> <!-- >-->
@if (item.canrenew) { @if (renewable && item.canrenew) {
<ion-button color="primary" (click)="onClick('renew')"> <ion-button color="primary" (click)="onClick('renew')">
{{ 'library.account.actions.renew.header' | translate }}</ion-button {{ 'library.account.actions.renew.header' | translate }}</ion-button
> >

View File

@@ -12,3 +12,6 @@
* You should have received a copy of the GNU General Public License along with * You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
ion-badge {
vertical-align: bottom;
}

View File

@@ -45,6 +45,8 @@ export class HoldsPageComponent {
? [PAIADocumentStatus.Reserved] ? [PAIADocumentStatus.Reserved]
: [PAIADocumentStatus.Ordered, PAIADocumentStatus.Provided]; : [PAIADocumentStatus.Ordered, PAIADocumentStatus.Provided];
try { try {
// Prepare patron (status) for the items
await this.libraryAccountService.getPatron();
this.paiaDocuments = await this.libraryAccountService.getFilteredItems(itemsStatus); this.paiaDocuments = await this.libraryAccountService.getFilteredItems(itemsStatus);
} catch { } catch {
await this.libraryAccountService.handleError(); await this.libraryAccountService.handleError();

View File

@@ -35,30 +35,20 @@
@switch (activeSegment) { @switch (activeSegment) {
@case ('orders') { @case ('orders') {
@for (hold of paiaDocuments; track hold) { @for (hold of paiaDocuments; track hold) {
@if (toNumber(hold.status) === paiaDocumentStatus.Provided) { <stapps-paia-item
<stapps-paia-item [item]="hold"
[item]="hold" [propertiesToShow]="['label', 'storage']"
[propertiesToShow]="['label', 'storage']" (documentAction)="onDocumentAction($event)"
(documentAction)="onDocumentAction($event)" listName="holds"
listName="holds" >
> </stapps-paia-item>
</stapps-paia-item>
} @else {
<stapps-paia-item
[item]="hold"
[propertiesToShow]="['label']"
(documentAction)="onDocumentAction($event)"
listName="holds"
>
</stapps-paia-item>
}
} }
} }
@case ('reservations') { @case ('reservations') {
@for (hold of paiaDocuments; track hold) { @for (hold of paiaDocuments; track hold) {
<stapps-paia-item <stapps-paia-item
[item]="hold" [item]="hold"
[propertiesToShow]="['label']" [propertiesToShow]="['label', 'starttime', 'storage', 'queue']"
(documentAction)="onDocumentAction($event)" (documentAction)="onDocumentAction($event)"
listName="holds" listName="holds"
> >

View File

@@ -20,7 +20,15 @@ import {
SCFeatureConfiguration, SCFeatureConfiguration,
SCFeatureConfigurationExtern, SCFeatureConfigurationExtern,
} from '@openstapps/core'; } from '@openstapps/core';
import {DocumentAction, PAIADocument, PAIADocumentStatus, PAIAFees, PAIAItems, PAIAPatron} from '../types'; import {
DocumentAction,
PAIADocument,
PAIADocumentStatus,
PAIAFees,
PAIAItems,
PAIAPatron,
PAIAPatronStatus,
} from '../types';
import {HebisDataProvider} from '../../hebis/hebis-data.provider'; import {HebisDataProvider} from '../../hebis/hebis-data.provider';
import {PAIATokenResponse} from '../../auth/paia/paia-token-response'; import {PAIATokenResponse} from '../../auth/paia/paia-token-response';
import {AuthHelperService} from '../../auth/auth-helper.service'; import {AuthHelperService} from '../../auth/auth-helper.service';
@@ -43,6 +51,11 @@ export class LibraryAccountService {
*/ */
authType: SCAuthorizationProviderType; authType: SCAuthorizationProviderType;
/**
* Account (Patron) status
*/
private status?: PAIAPatronStatus;
constructor( constructor(
protected requestor: Requestor = new JQueryRequestor(), protected requestor: Requestor = new JQueryRequestor(),
private readonly hebisDataProvider: HebisDataProvider, private readonly hebisDataProvider: HebisDataProvider,
@@ -60,12 +73,23 @@ export class LibraryAccountService {
this.authType = config.authProvider as SCAuthorizationProviderType; this.authType = config.authProvider as SCAuthorizationProviderType;
} }
async getProfile() { async getPatron() {
const patron = ((await this.getValidToken()) as PAIATokenResponse).patron; const patronId = ((await this.getValidToken()) as PAIATokenResponse).patron;
return { const patron = {
...(await this.performRequest<PAIAPatron>(`${this.baseUrl}/{patron}`)), ...(await this.performRequest<PAIAPatron>(`${this.baseUrl}/{patron}`)),
id: patron, id: patronId,
} as PAIAPatron; } as PAIAPatron;
// Refresh the status
this.status = Number(patron.status);
return patron;
}
async getPatronStatus() {
return this.status ?? (this.status = Number((await this.getPatron()).status) as PAIAPatronStatus);
}
async isActivePatron() {
return (await this.getPatronStatus()) === PAIAPatronStatus.Active;
} }
async getItems() { async getItems() {

View File

@@ -25,15 +25,17 @@ import {PAIAPatron} from '../../types';
export class ProfilePageComponent { export class ProfilePageComponent {
patron?: PAIAPatron; patron?: PAIAPatron;
propertiesToShow: (keyof PAIAPatron)[] = ['id', 'name', 'email', 'address', 'expires', 'note'];
constructor(private readonly libraryAccountService: LibraryAccountService) {} constructor(private readonly libraryAccountService: LibraryAccountService) {}
async ionViewWillEnter(): Promise<void> { async ionViewWillEnter(): Promise<void> {
try { try {
this.patron = await this.libraryAccountService.getProfile(); this.patron = await this.libraryAccountService.getPatron();
} catch { } catch {
await this.libraryAccountService.handleError(); await this.libraryAccountService.handleError();
} }
} }
isUnlimitedExpiry(date = ''): boolean {
return new Date(date).getFullYear() === 9999;
}
} }

View File

@@ -26,27 +26,45 @@
<ion-card> <ion-card>
<ion-card-content> <ion-card-content>
<ion-grid> <ion-grid>
@for (property of propertiesToShow; track property) { <ion-row>
@if (patron[property]) { <ion-col> {{ 'library.account.pages.profile.labels.id' | translate }}: </ion-col>
<ion-row> <ion-col>
<ion-col> {{ patron.id }}
{{ 'library.account.pages.profile.labels' + '.' + property | translate }}: </ion-col>
</ion-col> </ion-row>
<ion-col> <ion-row>
@if (!['expires'].includes(property)) { <ion-col> {{ 'library.account.pages.profile.labels.name' | translate }}: </ion-col>
{{ patron[property] }} <ion-col> {{ patron.name_details?.title ?? '' }} {{ patron.name }} </ion-col>
} @else { </ion-row>
@if (patron[property] === '9999-12-31') { @if (patron.email) {
{{ 'library.account.pages.profile.values.unlimited' | translate }} <ion-row>
} @else { <ion-col> {{ 'library.account.pages.profile.labels.email' | translate }}: </ion-col>
{{ 'library.account.pages.profile.values.expires' | translate }}:&nbsp;{{ <ion-col>
patron[property] | amDateFormat: 'll' {{ patron.email }}
}} </ion-col>
} </ion-row>
} }
</ion-col> @if (patron.address) {
</ion-row> <ion-row>
} <ion-col> {{ 'library.account.pages.profile.labels.address' | translate }}: </ion-col>
<ion-col>
{{ patron.address }}
</ion-col>
</ion-row>
}
@if (patron.expires) {
<ion-row>
<ion-col> {{ 'library.account.pages.profile.labels.expires' | translate }}: </ion-col>
<ion-col>
@if (isUnlimitedExpiry(patron.expires)) {
{{ 'library.account.pages.profile.values.unlimited' | translate }}
} @else {
{{ 'library.account.pages.profile.values.expires' | translate }}:&nbsp;{{
patron.expires | amDateFormat: 'll'
}}
}
</ion-col>
</ion-row>
} }
</ion-grid> </ion-grid>
</ion-card-content> </ion-card-content>

View File

@@ -19,9 +19,24 @@ export interface PAIAPatron {
email?: string; email?: string;
address?: string; address?: string;
expires?: string; expires?: string;
status?: string; status?: PAIAPatronStatus;
type?: string; type?: string;
note?: string; note?: string;
// HeBIS specific property (not in PAIA documentation)
name_details?: {
firstname?: string;
lastname?: string;
gender?: string;
title?: string;
};
}
export enum PAIAPatronStatus {
Active = 0,
Inactive = 1,
InactiveExpired = 2,
InactiveOutstandingFees = 3,
InactiveExpiredOutstandingFees = 4,
} }
/* /*
@@ -39,7 +54,7 @@ export interface PAIADocument {
queue?: string; queue?: string;
renewals?: string; renewals?: string;
reminder?: string; reminder?: string;
endtime?: string; starttime?: string;
duedate?: string; duedate?: string;
cancancel?: boolean; cancancel?: boolean;
canrenew?: boolean; canrenew?: boolean;
@@ -80,3 +95,9 @@ export interface DocumentAction {
action: 'cancel' | 'renew'; action: 'cancel' | 'renew';
doc: PAIADocument; doc: PAIADocument;
} }
export interface PAIADocumentVisualStatus {
color: 'warning' | 'success';
status: PAIADocumentStatus;
statusText: 'ordered' | 'ready';
}

View File

@@ -1,5 +1,6 @@
import {Directive, Input, Host} from '@angular/core'; import {Directive, Input, Host} from '@angular/core';
import {MapComponent} from '@maplibre/ngx-maplibre-gl'; import {MapComponent} from '@maplibre/ngx-maplibre-gl';
import {environment} from '../../../environments/environment';
@Directive({ @Directive({
selector: 'mgl-map[styleName]', selector: 'mgl-map[styleName]',
@@ -10,7 +11,7 @@ export class MapStyleDirective {
@Input() @Input()
set styleName(name: string) { set styleName(name: string) {
const style = `https://maps.server.uni-frankfurt.de/static/styles/${name}/style.json`; const style = `${environment.backend_url}/_static/map/styles/${name}/style.json`;
if (this.map.style) { if (this.map.style) {
this.map.mapInstance.setStyle(style); this.map.mapInstance.setStyle(style);
} else { } else {

View File

@@ -1,10 +1,14 @@
import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; import {ChangeDetectionStrategy, Component, ElementRef, Input} from '@angular/core';
import {SCIdCard} from '@openstapps/core'; import {SCIdCard} from '@openstapps/core';
import {FullScreenImageDirective} from '../../util/full-screen-image.directive';
import {ThingTranslateModule} from '../../translation/thing-translate.module'; import {ThingTranslateModule} from '../../translation/thing-translate.module';
import {AsyncPipe, TitleCasePipe} from '@angular/common'; import {AsyncPipe, TitleCasePipe} from '@angular/common';
import {InRangeNowPipe, ToDateRangePipe} from '../../util/in-range.pipe'; import {IntervalIsNowPipe, ToDateIntervalPipe} from '../../util/in-range.pipe';
import {TranslateModule} from '@ngx-translate/core'; import {TranslateModule} from '@ngx-translate/core';
import {AnimationController, ModalController} from '@ionic/angular';
import {ScreenBrightness} from '@capacitor-community/screen-brightness';
import {ScreenOrientation} from '@capacitor/screen-orientation';
import {Capacitor} from '@capacitor/core';
import {iosDuration, iosEasing, mdDuration, mdEasing} from 'src/app/animation/easings';
@Component({ @Component({
selector: 'stapps-id-card', selector: 'stapps-id-card',
@@ -12,12 +16,10 @@ import {TranslateModule} from '@ngx-translate/core';
styleUrls: ['id-card.scss'], styleUrls: ['id-card.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true, standalone: true,
hostDirectives: [FullScreenImageDirective],
imports: [ imports: [
FullScreenImageDirective,
ThingTranslateModule, ThingTranslateModule,
InRangeNowPipe, IntervalIsNowPipe,
ToDateRangePipe, ToDateIntervalPipe,
AsyncPipe, AsyncPipe,
TranslateModule, TranslateModule,
TitleCasePipe, TitleCasePipe,
@@ -25,4 +27,126 @@ import {TranslateModule} from '@ngx-translate/core';
}) })
export class IdCardComponent { export class IdCardComponent {
@Input({required: true}) item: SCIdCard; @Input({required: true}) item: SCIdCard;
constructor(
private modalController: ModalController,
private animationController: AnimationController,
private elementRef: ElementRef,
) {}
async presentFullscreen() {
const top = await this.modalController.getTop();
const mode = document.querySelector('ion-app')!.getAttribute('mode');
if (top) return;
if (window.innerWidth >= 768) return;
if (Capacitor.isNativePlatform()) {
const orientation = await ScreenOrientation.orientation();
ScreenOrientation.lock({orientation: 'portrait'});
if (orientation.type.startsWith('landscape')) {
await new Promise(resolve => setTimeout(resolve, 500));
}
}
const img: HTMLImageElement = this.elementRef.nativeElement.querySelector('img')!;
const safeArea = (location: string) =>
Number(img.computedStyleMap().get(`--ion-safe-area-${location}`)!.toString().replace(/px$/, ''));
const safeAreaTop = safeArea('top');
const safeAreaBottom = safeArea('bottom');
const safeAreaLeft = safeArea('left');
const safeAreaRight = safeArea('right');
const windowWidth = window.innerWidth - safeAreaLeft - safeAreaRight;
const windowHeight = window.innerHeight - safeAreaTop - safeAreaBottom;
const isLandscape = windowWidth > windowHeight;
const nativeBounds = img.getBoundingClientRect();
const imageAspect = nativeBounds.width / nativeBounds.height;
const imgWidth = Math.min(windowHeight, windowWidth * imageAspect);
const imgHeight = imgWidth / imageAspect;
const fullscreenWidth = isLandscape ? imgHeight : imgWidth;
const fullscreenHeight = isLandscape ? imgWidth : imgHeight;
const scale = fullscreenWidth / windowWidth;
const animation = (modal: HTMLElement, leave = false) => {
const root = modal.shadowRoot!;
const sourceAnimation = this.animationController
.create()
.addElement(this.elementRef.nativeElement)
.fromTo('opacity', 0, 0);
const backdrop =
'linear-gradient(to bottom,' +
'transparent 16px,' +
'rgba(0, 0, 0, 0.3) 20%,' +
'rgba(0, 0, 0, 0.3) 80%,' +
'transparent 100%)';
const wrapperAnimation = this.animationController
.create()
.beforeStyles({'--background': 'transparent', 'margin': '0'})
.addElement(root.querySelector('.modal-wrapper')!)
.fromTo('transform', 'scale(1)', 'scale(1)')
.fromTo('opacity', '1', '1');
const backdropAnimation = this.animationController
.create()
.beforeStyles({background: backdrop, filter: 'blur(16px)'})
.addElement(root.querySelector('ion-backdrop')!)
.fromTo('opacity', leave ? 1 : 0, leave ? 0 : 1);
const small =
`translate(${nativeBounds.left - safeAreaLeft}px, ${nativeBounds.top - safeAreaTop}px) ` +
`scale(${nativeBounds.width / windowWidth}) ` +
`rotate(0) `;
const large =
`translate(${windowWidth - (windowWidth - fullscreenHeight) / 2}px, ${
(windowHeight - fullscreenWidth) / 2
}px)` +
`scale(${scale}) ` +
`rotate(${isLandscape ? 0 : 90}deg) `;
const cardAnimation = this.animationController
.create()
.addElement(modal.querySelector('stapps-id-card')!)
.beforeStyles({
'transform-origin': 'top left',
'filter': 'drop-shadow(0 0 16px rgba(0, 0, 0, 0.1))',
})
.fromTo('transform', leave ? large : small, leave ? small : large);
return this.animationController
.create()
.addElement(modal)
.easing(mode === 'ios' ? iosEasing : mdEasing)
.duration(2 * (mode === 'ios' ? iosDuration : mdDuration))
.addAnimation([wrapperAnimation, backdropAnimation, cardAnimation, sourceAnimation]);
};
const modal = await this.modalController.create({
component: IdCardComponent,
backdropDismiss: true,
mode: 'md',
componentProps: {
item: this.item,
},
presentingElement: this.elementRef.nativeElement,
enterAnimation: base => animation(base),
leaveAnimation: base => animation(base, true),
});
const dismiss = () => modal.dismiss();
window.addEventListener('click', dismiss);
modal.addEventListener('didDismiss', () => window.removeEventListener('click', dismiss));
if (Capacitor.isNativePlatform()) {
const brightness = (await ScreenBrightness.getBrightness()).brightness;
ScreenBrightness.setBrightness({brightness: 1});
modal.addEventListener('didDismiss', () => {
ScreenOrientation.unlock();
ScreenBrightness.setBrightness({brightness: brightness === 1 ? 0.5 : brightness});
});
}
await modal.present();
}
} }

View File

@@ -1,4 +1,9 @@
<img [src]="item.image" [alt]="'name' | thingTranslate: item" draggable="false" /> <img
@if (item.validity && (item.validity | toDateRange | isInRangeNow | async) === false) { [src]="item.image"
[alt]="'name' | thingTranslate: item"
draggable="false"
(click)="presentFullscreen()"
/>
@if (item.validity && (item.validity | rangeToDateInterval | dfnsIntervalIsNow | async) === false) {
<div class="expired">{{ 'profile.userInfo.expired' | translate | titlecase }}</div> <div class="expired">{{ 'profile.userInfo.expired' | translate | titlecase }}</div>
} }

View File

@@ -3,11 +3,6 @@
overflow: hidden; overflow: hidden;
} }
:host:fullscreen {
margin: 0;
padding: 0;
}
img { img {
border-radius: 3mm; border-radius: 3mm;
} }

View File

@@ -19,7 +19,6 @@ import {IonicModule} from '@ionic/angular';
import {AsyncPipe, TitleCasePipe} from '@angular/common'; import {AsyncPipe, TitleCasePipe} from '@angular/common';
import {ThingTranslateModule} from '../../translation/thing-translate.module'; import {ThingTranslateModule} from '../../translation/thing-translate.module';
import {UtilModule} from '../../util/util.module'; import {UtilModule} from '../../util/util.module';
import {FullScreenImageDirective} from '../../util/full-screen-image.directive';
import {IdCardComponent} from './id-card.component'; import {IdCardComponent} from './id-card.component';
import {TranslateModule} from '@ngx-translate/core'; import {TranslateModule} from '@ngx-translate/core';
import {Observable} from 'rxjs'; import {Observable} from 'rxjs';
@@ -36,7 +35,6 @@ import {Observable} from 'rxjs';
AsyncPipe, AsyncPipe,
ThingTranslateModule, ThingTranslateModule,
UtilModule, UtilModule,
FullScreenImageDirective,
IdCardComponent, IdCardComponent,
TranslateModule, TranslateModule,
TitleCasePipe, TitleCasePipe,

View File

@@ -1,10 +1,11 @@
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {SCIdCard, SCThingOriginType, SCThingType, SCUserConfiguration} from '@openstapps/core'; import {SCIdCard, SCThingOriginType, SCThingType, SCUserConfiguration} from '@openstapps/core';
import {from, Observable, of} from 'rxjs'; import {from, of, Observable} from 'rxjs';
import {AuthHelperService} from '../auth/auth-helper.service'; import {AuthHelperService} from '../auth/auth-helper.service';
import {mergeMap, filter, map, startWith} from 'rxjs/operators'; import {mergeMap, concatWith, filter, map, startWith, catchError, tap} from 'rxjs/operators';
import {ConfigProvider} from '../config/config.provider'; import {ConfigProvider} from '../config/config.provider';
import {HttpClient} from '@angular/common/http'; import {HttpClient} from '@angular/common/http';
import {EncryptedStorageProvider} from '../storage/encrypted-storage.provider';
@Injectable({providedIn: 'root'}) @Injectable({providedIn: 'root'})
export class IdCardsProvider { export class IdCardsProvider {
@@ -12,18 +13,27 @@ export class IdCardsProvider {
private authHelper: AuthHelperService, private authHelper: AuthHelperService,
private config: ConfigProvider, private config: ConfigProvider,
private httpClient: HttpClient, private httpClient: HttpClient,
private encryptedStorageProvider: EncryptedStorageProvider,
) {} ) {}
getIdCards(): Observable<SCIdCard[]> { getIdCards(): Observable<SCIdCard[]> {
const feature = this.config.config.app.features.extern?.['idCards']; const feature = this.config.config.app.features.extern?.['idCards'];
const auth = this.authHelper.getProvider(feature?.authProvider ?? 'default'); const auth = this.authHelper.getProvider(feature?.authProvider ?? 'default');
const storedIdCards = from(
this.encryptedStorageProvider.get<SCIdCard[]>('id-cards') as Promise<SCIdCard[]>,
).pipe(filter(it => it !== undefined));
return auth.isAuthenticated$.pipe( return auth.isLoggedIn$.pipe(
mergeMap(isAuthenticated => mergeMap(isLoggedIn =>
isAuthenticated isLoggedIn
? feature ? feature
? from(auth.getValidToken()).pipe( ? storedIdCards.pipe(
mergeMap(token => this.fetchIdCards(feature.url, token.accessToken)), concatWith(
from(auth.getValidToken()).pipe(
mergeMap(token => this.fetchIdCards(feature.url, token.accessToken)),
catchError(() => storedIdCards),
),
),
) )
: auth.user$.pipe( : auth.user$.pipe(
filter(user => user !== undefined), filter(user => user !== undefined),
@@ -31,19 +41,20 @@ export class IdCardsProvider {
mergeMap(user => this.fetchFallbackIdCards(user)), mergeMap(user => this.fetchFallbackIdCards(user)),
startWith([]), startWith([]),
) )
: // TODO: find a better solution here (async pipe stuff...) : of([]).pipe(tap({next: () => this.encryptedStorageProvider.delete('id-cards')})),
of([]),
), ),
); );
} }
private fetchIdCards(url: string, token: string): Observable<SCIdCard[]> { private fetchIdCards(url: string, token: string): Observable<SCIdCard[]> {
return this.httpClient.get<SCIdCard[]>(url, { return this.httpClient
headers: { .get<SCIdCard[]>(url, {
Authorization: `Bearer ${token}`, headers: {
}, Authorization: `Bearer ${token}`,
responseType: 'json', },
}); responseType: 'json',
})
.pipe(tap({next: idCards => this.encryptedStorageProvider.set('id-cards', idCards)}));
} }
private fetchFallbackIdCards(user: SCUserConfiguration): Observable<SCIdCard[]> { private fetchFallbackIdCards(user: SCUserConfiguration): Observable<SCIdCard[]> {

View File

@@ -4,9 +4,10 @@ import {HttpClient} from '@angular/common/http';
import {AuthHelperService} from '../auth/auth-helper.service'; import {AuthHelperService} from '../auth/auth-helper.service';
import {BehaviorSubject, firstValueFrom, of} from 'rxjs'; import {BehaviorSubject, firstValueFrom, of} from 'rxjs';
import {SCAuthorizationProviderType} from '@openstapps/core'; import {SCAuthorizationProviderType} from '@openstapps/core';
import {EncryptedStorageProvider} from '../storage/encrypted-storage.provider';
class FakeAuth { class FakeAuth {
isAuthenticated$ = new BehaviorSubject(false); isLoggedIn$ = new BehaviorSubject(false);
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function
getValidToken() {} getValidToken() {}
@@ -16,6 +17,7 @@ describe('IdCards', () => {
let configProvider: ConfigProvider; let configProvider: ConfigProvider;
let httpClient: HttpClient; let httpClient: HttpClient;
let authHelper: AuthHelperService; let authHelper: AuthHelperService;
let encryptedStorageProvider: EncryptedStorageProvider;
let fakeAuth: FakeAuth; let fakeAuth: FakeAuth;
beforeEach(() => { beforeEach(() => {
@@ -27,19 +29,23 @@ describe('IdCards', () => {
fakeAuth = new FakeAuth(); fakeAuth = new FakeAuth();
authHelper = jasmine.createSpyObj('AuthHelperService', ['getProvider']); authHelper = jasmine.createSpyObj('AuthHelperService', ['getProvider']);
authHelper.getProvider = jasmine.createSpy().and.returnValue(fakeAuth); authHelper.getProvider = jasmine.createSpy().and.returnValue(fakeAuth);
encryptedStorageProvider = jasmine.createSpyObj('EncryptedStorageProvider', ['get', 'set', 'delete']);
encryptedStorageProvider.get = jasmine.createSpy().and.resolveTo();
encryptedStorageProvider.set = jasmine.createSpy().and.resolveTo();
encryptedStorageProvider.delete = jasmine.createSpy().and.resolveTo();
}); });
it('should emit undefined if not logged in', async () => { it('should emit undefined if not logged in', async () => {
const provider = new IdCardsProvider(authHelper, configProvider, httpClient); const provider = new IdCardsProvider(authHelper, configProvider, httpClient, encryptedStorageProvider);
expect(await firstValueFrom(provider.getIdCards())).toEqual([]); expect(await firstValueFrom(provider.getIdCards())).toEqual([]);
expect(authHelper.getProvider).toHaveBeenCalledOnceWith('fakeAuth' as SCAuthorizationProviderType); expect(authHelper.getProvider).toHaveBeenCalledOnceWith('fakeAuth' as SCAuthorizationProviderType);
}); });
it('should emit network result when logged in', async () => { it('should emit network result when logged in', async () => {
fakeAuth.isAuthenticated$.next(true); fakeAuth.isLoggedIn$.next(true);
httpClient.get = jasmine.createSpy().and.returnValue(of(['abc'])); httpClient.get = jasmine.createSpy().and.returnValue(of(['abc']));
fakeAuth.getValidToken = jasmine.createSpy().and.resolveTo({accessToken: 'fake-token'}); fakeAuth.getValidToken = jasmine.createSpy().and.resolveTo({accessToken: 'fake-token'});
const provider = new IdCardsProvider(authHelper, configProvider, httpClient); const provider = new IdCardsProvider(authHelper, configProvider, httpClient, encryptedStorageProvider);
expect(await firstValueFrom(provider.getIdCards())).toEqual(['abc' as never]); expect(await firstValueFrom(provider.getIdCards())).toEqual(['abc' as never]);
expect(authHelper.getProvider).toHaveBeenCalledOnceWith('fakeAuth' as SCAuthorizationProviderType); expect(authHelper.getProvider).toHaveBeenCalledOnceWith('fakeAuth' as SCAuthorizationProviderType);
// eslint-disable-next-line unicorn/no-null // eslint-disable-next-line unicorn/no-null
@@ -52,12 +58,12 @@ describe('IdCards', () => {
}); });
it('should react to logins', async () => { it('should react to logins', async () => {
const provider = new IdCardsProvider(authHelper, configProvider, httpClient); const provider = new IdCardsProvider(authHelper, configProvider, httpClient, encryptedStorageProvider);
const observable = provider.getIdCards(); const observable = provider.getIdCards();
expect(await firstValueFrom(observable)).toEqual([]); expect(await firstValueFrom(observable)).toEqual([]);
httpClient.get = jasmine.createSpy().and.returnValue(of(['abc'])); httpClient.get = jasmine.createSpy().and.returnValue(of(['abc']));
fakeAuth.getValidToken = jasmine.createSpy().and.resolveTo({accessToken: 'fake-token'}); fakeAuth.getValidToken = jasmine.createSpy().and.resolveTo({accessToken: 'fake-token'});
fakeAuth.isAuthenticated$.next(true); fakeAuth.isLoggedIn$.next(true);
// this is counter-intuitive, but because we unsubscribed above the first value // this is counter-intuitive, but because we unsubscribed above the first value
// will now contain the network result. // will now contain the network result.
expect(await firstValueFrom(observable)).toEqual(['abc' as never]); expect(await firstValueFrom(observable)).toEqual(['abc' as never]);

View File

@@ -6,74 +6,56 @@
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * FITNESS FOR A PARTICULAr purpose. see the gnu general public license for
* more details. * more details.
* *
* You should have received a copy of the GNU General Public License along with * you should have received a copy of the gnu general public license along with
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. if not, see <https://www.gnu.org/licenses/>.
*/ */
import {Component, DestroyRef, inject, Input, OnInit} from '@angular/core'; import {Component, inject, input} from '@angular/core';
import {SCSection} from '../../../../config/profile-page-sections'; import {SCSection} from '../../../../config/profile-page-sections';
import {AuthHelperService} from '../../auth/auth-helper.service'; import {AuthHelperService} from '../../auth/auth-helper.service';
import {Observable} from 'rxjs'; import {mergeMap, of} from 'rxjs';
import {SCAuthorizationProviderType} from '@openstapps/core';
import Swiper from 'swiper'; import Swiper from 'swiper';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; import {toObservable, toSignal} from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'stapps-profile-page-section', selector: 'stapps-profile-page-section',
templateUrl: 'profile-page-section.html', templateUrl: 'profile-page-section.html',
styleUrls: ['profile-page-section.scss'], styleUrls: ['profile-page-section.scss'],
}) })
export class ProfilePageSectionComponent implements OnInit { export class ProfilePageSectionComponent {
@Input() item: SCSection; item = input.required<SCSection>();
@Input() minSlideWidth = 110; minSlideWidth = input(110);
isLoggedIn: boolean; authHelper = inject(AuthHelperService);
loggedIn = toSignal(
toObservable(this.item).pipe(
mergeMap(item =>
item.authProvider ? this.authHelper.getProvider(item.authProvider).isLoggedIn$ : of(false),
),
),
);
isEnd = false; isEnd = false;
isBeginning = true; isBeginning = true;
slidesPerView: number; slidesPerView?: number;
slidesFillScreen = false; slidesFillScreen = false;
data: {
[key in SCAuthorizationProviderType]: {loggedIn$: Observable<boolean>};
} = {
default: {
loggedIn$: this.authHelper.getProvider('default').isAuthenticated$,
},
paia: {
loggedIn$: this.authHelper.getProvider('paia').isAuthenticated$,
},
};
destroy$ = inject(DestroyRef);
constructor(private authHelper: AuthHelperService) {}
ngOnInit() {
if (this.item.authProvider) {
this.data[this.item.authProvider].loggedIn$
.pipe(takeUntilDestroyed(this.destroy$))
.subscribe(loggedIn => {
this.isLoggedIn = loggedIn;
});
}
}
activeIndexChange(swiper: Swiper) { activeIndexChange(swiper: Swiper) {
this.isBeginning = swiper.isBeginning; this.isBeginning = swiper.isBeginning;
this.isEnd = swiper.isEnd; this.isEnd = swiper.isEnd;
this.slidesFillScreen = this.slidesPerView >= swiper.slides.length; this.slidesFillScreen = this.slidesPerView! >= swiper.slides.length;
} }
resizeSwiper(resizeEvent: ResizeObserverEntry, swiper: Swiper) { resizeSwiper(resizeEvent: ResizeObserverEntry, swiper: Swiper) {
const slidesPerView = const slidesPerView =
Math.floor((resizeEvent.contentRect.width - this.minSlideWidth / 2) / this.minSlideWidth) + 0.5; Math.floor((resizeEvent.contentRect.width - this.minSlideWidth() / 2) / this.minSlideWidth()) + 0.5;
if (slidesPerView > 1 && slidesPerView !== this.slidesPerView) { if (slidesPerView > 1 && slidesPerView !== this.slidesPerView) {
this.slidesPerView = slidesPerView; this.slidesPerView = slidesPerView;
@@ -84,16 +66,13 @@ export class ProfilePageSectionComponent implements OnInit {
} }
async toggleLogIn() { async toggleLogIn() {
if (!this.item.authProvider) return; const providerType = this.item().authProvider;
await (this.isLoggedIn ? this.signOut(this.item.authProvider) : this.signIn(this.item.authProvider)); if (!providerType) return;
} if (this.loggedIn()) {
await this.authHelper.getProvider(providerType).signOut();
async signIn(providerType: SCAuthorizationProviderType) { await this.authHelper.endBrowserSession(providerType);
await this.authHelper.getProvider(providerType).signIn(); } else {
} await this.authHelper.getProvider(providerType).signIn();
}
async signOut(providerType: SCAuthorizationProviderType) {
await this.authHelper.getProvider(providerType).signOut();
await this.authHelper.endBrowserSession(providerType);
} }
} }

View File

@@ -13,24 +13,24 @@
~ this program. If not, see <https://www.gnu.org/licenses/>. ~ this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
<stapps-section [title]="'name' | translateSimple: item"> <stapps-section [title]="'name' | translateSimple: item()">
@if (item.authProvider) { @if (item().authProvider) {
<ion-button slot="button-end" fill="clear" color="dark" (click)="toggleLogIn()"> <ion-button slot="button-end" fill="clear" color="dark" (click)="toggleLogIn()">
@if (isLoggedIn) { @if (loggedIn()) {
<ion-icon slot="end" name="logout"></ion-icon> <ion-icon slot="end" name="logout"></ion-icon>
} @else { } @else {
<ion-icon slot="end" name="login"></ion-icon> <ion-icon slot="end" name="login"></ion-icon>
} }
<ion-label>{{ 'profile.buttons.default.log_' + (isLoggedIn ? 'out' : 'in') | translate }}</ion-label> <ion-label>{{ 'profile.buttons.default.log_' + (loggedIn() ? 'out' : 'in') | translate }}</ion-label>
</ion-button> </ion-button>
} }
<simple-swiper> <simple-swiper>
@for (link of item.links; track link) { @for (link of item().links; track link) {
<ion-item <ion-item
lines="none" lines="none"
[routerLink]="link.link" [routerLink]="link.link"
[disabled]="link.needsAuth && !isLoggedIn" [disabled]="link.needsAuth && !loggedIn()"
[detail]="false" [detail]="false"
> >
<div> <div>

View File

@@ -0,0 +1,92 @@
import {Injectable} from '@angular/core';
import {StorageProvider} from './storage.provider';
import {Capacitor} from '@capacitor/core';
import {SecureStoragePlugin} from 'capacitor-secure-storage-plugin';
@Injectable({providedIn: 'root'})
export class EncryptedStorageProvider {
constructor(private storageProvider: StorageProvider) {}
/**
* Retrieve a large value from an encrypted storage
* Also returns undefined if a secure context is not available (i.e. web).
* @param key Unique identifier of the wanted resource in storage
* @returns The value of the resource, if found
*/
async get<T>(key: string): Promise<T | undefined> {
if (!Capacitor.isNativePlatform()) return undefined;
try {
const jwt = JSON.parse((await SecureStoragePlugin.get({key: `stapps:key:${key}`})).value);
const aesKey = await crypto.subtle.importKey('jwk', jwt, {name: 'AES-GCM'}, true, [
'encrypt',
'decrypt',
]);
const iv = await this.storageProvider.get<ArrayBuffer>(`encrypted:${key}:iv`);
const encryptedIdCards = await this.storageProvider.get<ArrayBuffer>(`encrypted:${key}`);
const decrypted = await crypto.subtle.decrypt({name: 'AES-GCM', iv}, aesKey, encryptedIdCards);
const decompressionStream = new DecompressionStream('gzip');
const writer = decompressionStream.writable.getWriter();
writer.write(decrypted);
writer.close();
const decompressed = await new Response(decompressionStream.readable).arrayBuffer();
return JSON.parse(new TextDecoder().decode(decompressed));
} catch (error) {
console.warn(error);
return undefined;
}
}
/**
* Store a large value in an encrypted storage
* Does nothing if a secure context is not available (i.e. web).
* @param key Unique identifier of the resource in storage
* @param value The value to store
* @returns A promise that resolves when the value is stored
*/
async set<T>(key: string, value: T): Promise<void> {
if (!Capacitor.isNativePlatform()) return undefined;
try {
const compressionStream = new CompressionStream('gzip');
const writer = compressionStream.writable.getWriter();
writer.write(new TextEncoder().encode(JSON.stringify(value)));
writer.close();
const encoded = await new Response(compressionStream.readable).arrayBuffer();
const iv = crypto.getRandomValues(new Uint8Array(16));
const aesKey = await crypto.subtle.generateKey({name: 'AES-GCM', length: 256}, true, [
'encrypt',
'decrypt',
]);
await Promise.all([
SecureStoragePlugin.set({
key: `stapps:key:${key}`,
value: JSON.stringify(await crypto.subtle.exportKey('jwk', aesKey)),
}),
this.storageProvider.put(`encrypted:${key}:iv`, iv),
]);
this.storageProvider.put<ArrayBuffer>(
`encrypted:${key}`,
await crypto.subtle.encrypt({name: 'AES-GCM', iv}, aesKey, encoded),
);
} catch (error) {
alert(error);
}
}
async delete(key: string): Promise<void> {
if (!Capacitor.isNativePlatform()) return;
await Promise.all([
SecureStoragePlugin.remove({key: `stapps:key:${key}`}),
this.storageProvider.delete(`encrypted:${key}:iv`),
this.storageProvider.delete(`encrypted:${key}`),
]);
}
}

View File

@@ -1,7 +1,7 @@
import {Pipe, PipeTransform} from '@angular/core'; import {Pipe, PipeTransform} from '@angular/core';
import {SCRange, isInRange, SCISO8601DateRange} from '@openstapps/core'; import {SCRange, isInRange, SCISO8601DateRange} from '@openstapps/core';
import {merge, Observable, timer} from 'rxjs'; import {NormalizedInterval, differenceInMilliseconds, interval} from 'date-fns';
import {distinctUntilChanged, map, startWith} from 'rxjs/operators'; import {EMPTY, Observable, SchedulerLike, asyncScheduler, concat, defer, map, of, timer} from 'rxjs';
@Pipe({ @Pipe({
name: 'isInRange', name: 'isInRange',
@@ -14,28 +14,51 @@ export class InRangePipe implements PipeTransform {
} }
} }
export const MIN_DATE = new Date(0);
export const MAX_DATE = new Date(1e15);
// Maximum safe delay for JavaScript timers (~24.8 days)
export const MAX_DELAY = 2 ** 31 - 1;
@Pipe({ @Pipe({
name: 'toDateRange', name: 'rangeToDateInterval',
pure: true, pure: true,
standalone: true, standalone: true,
}) })
export class ToDateRangePipe implements PipeTransform { export class ToDateIntervalPipe implements PipeTransform {
transform(value: SCISO8601DateRange): SCRange<Date> { transform(value: SCISO8601DateRange): NormalizedInterval {
return Object.fromEntries(Object.entries(value).map(([key, value]) => [key, new Date(value)])); return interval(new Date(value.gte ?? value.gt ?? MIN_DATE), new Date(value.lte ?? value.lt ?? MAX_DATE));
} }
} }
/**
* Returns an Observable that will change its value when the current date is within the given interval.
*/
export function isWithinIntervalObservable(
value: NormalizedInterval,
scheduler: SchedulerLike = asyncScheduler,
): Observable<boolean> {
return defer(() => {
const now = scheduler.now();
const activate = differenceInMilliseconds(value.start, now);
const deactivate = differenceInMilliseconds(value.end, now);
return concat(
of(activate <= 0 && deactivate > 0),
activate <= 0 ? EMPTY : timer(value.start, scheduler).pipe(map(() => true)),
differenceInMilliseconds(value.end, now) >= MAX_DELAY || deactivate <= 0
? EMPTY
: timer(value.end, scheduler).pipe(map(() => false)),
);
});
}
@Pipe({ @Pipe({
name: 'isInRangeNow', name: 'dfnsIntervalIsNow',
pure: true, pure: true,
standalone: true, standalone: true,
}) })
export class InRangeNowPipe implements PipeTransform { export class IntervalIsNowPipe implements PipeTransform {
transform(value: SCRange<Date>): Observable<boolean> { transform(value: NormalizedInterval): Observable<boolean> {
return merge(timer(value.lte || value.lt || 0), timer(value.gte || value.gt || 0)).pipe( return isWithinIntervalObservable(value);
startWith(0),
map(() => isInRange(new Date(), value)),
distinctUntilChanged(),
);
} }
} }

View File

@@ -0,0 +1,46 @@
import {TestScheduler} from 'rxjs/testing';
import {MAX_DATE, MIN_DATE, isWithinIntervalObservable} from './in-range.pipe';
import {interval} from 'date-fns';
import {MAX_DELAY} from './in-range.pipe';
/**
* Test macro
*/
function test(range: [number | undefined, number | undefined], subscribe: string, expected: string) {
const testScheduler = new TestScheduler((actual, expected) => {
expect(actual).withContext(actual.map(JSON.stringify).join('\n')).toEqual(expected);
});
it(`should emit "${expected}" when "${subscribe}" for range ${range[0] ?? ''}..${range[1] ?? ''}`, () => {
testScheduler.run(({expectObservable}) => {
expectObservable(
isWithinIntervalObservable(
interval(new Date(range[0] ?? MIN_DATE), new Date(range[1] ?? MAX_DATE)),
testScheduler,
),
subscribe,
).toBe(expected, {t: true, f: false});
});
});
}
describe('isWithinIntervalObservable', () => {
test([500, undefined], '1s ^', '1s (t|)');
test([1000, undefined], '500ms ^', '500ms f 499ms (t|)');
test([undefined, 500], '1s ^', '1s (f|)');
test([undefined, 1000], '500ms ^', '500ms t 499ms (f|)');
test([1000, 2000], '500ms ^', '500ms f 499ms t 999ms (f|)');
test([500, 1000], '1500ms ^', '1500ms (f|)');
test([500, 1000], '1s ^', '1000ms (f|)');
test([500, 1000], '999ms ^', '999ms t (f|)');
test([500, 1000], '500ms ^', '500ms t 499ms (f|)');
test([500, 1000], '499ms ^', '499ms f t 499ms (f|)');
test([500, 1000], '^ 750ms !', 'f 499ms t');
// Long interval test case: emit `true` and then complete (EMPTY) because `end` is beyond the delay limit
test([500, 500 + MAX_DELAY + 2000], '1s ^', '1s (t|)');
});

View File

@@ -1,221 +0,0 @@
## [2.1.1](https://gitlab.com/openstapps/app/compare/v2.0.1...v2.1.1) (2023-05-10)
### Bug Fixes
- autofocus searchbar only when no default data is displayed ([e90286f](https://gitlab.com/openstapps/app/commit/e90286fc6814f5c40af3e297be42f23128b991be))
- breadcrumbs are under parallax ([9e160e8](https://gitlab.com/openstapps/app/commit/9e160e8d1ee9409e4fbe518c9dd9748705e680e1))
- browser logout only if endSession url defined ([7f6de94](https://gitlab.com/openstapps/app/commit/7f6de94ab572be66d7f10758c37dcf10af46b4e0)), closes [#395](https://gitlab.com/openstapps/app/issues/395)
- can't select some list elements on safari 16.4 ([3e99d7f](https://gitlab.com/openstapps/app/commit/3e99d7fa8fcae4538e1afe6a017570b0cb6ff45e))
- canteen view removes item select listener on view exit ([05e996a](https://gitlab.com/openstapps/app/commit/05e996ae9052b11c23dc1093ef526f08e3e2e6b6))
- catalog module semester selection ([afbd1fc](https://gitlab.com/openstapps/app/commit/afbd1fc048997acbc0113f8957016f8947b58626))
- catalog semester selection ([a8c7d5a](https://gitlab.com/openstapps/app/commit/a8c7d5ab5934adf853cc40c1df311178df54057e))
- data detail local favorite fallback causing duplicate nested favorite view ([dff4a95](https://gitlab.com/openstapps/app/commit/dff4a95acc55574b4872b0e4e39555cce0b2fd60))
- data-detail favorite button color leaking to list items ([95f2da2](https://gitlab.com/openstapps/app/commit/95f2da2def39ec56f40a660a63e5f7fddb4b3d53))
- location flow on iOS devices ([f207e02](https://gitlab.com/openstapps/app/commit/f207e029f1b30624bf411a57b3c552ef259e13ed))
- parallax broken since safari 16.4 ([0f970fa](https://gitlab.com/openstapps/app/commit/0f970fa1f164a310e24a85140d8bf0da21e5a5f1))
- remove infinite scroll e2e test ([47565e5](https://gitlab.com/openstapps/app/commit/47565e51b0dda5c8227238b6dc19f0d277408429))
- replace breadcrumb popover with simply expanding the breadcrumbs ([1318cbc](https://gitlab.com/openstapps/app/commit/1318cbca7f0cf72e10d96fff1a1116ba073fe8dc))
- schedule tabs navigating to the wrong url ([abf2ab6](https://gitlab.com/openstapps/app/commit/abf2ab6a5a94941d439adf54ec677332823892db))
- translate simple pipe doesn't update on language changes ([f5ca150](https://gitlab.com/openstapps/app/commit/f5ca1508fb9d95693615bfb9e03bc127bd83be00))
- typo in catalog provider query ([f49c44f](https://gitlab.com/openstapps/app/commit/f49c44f5c53780e4794dc1ef4cbacfb20cabbd97))
- workaround for side menu items not being active on page load ([947cab4](https://gitlab.com/openstapps/app/commit/947cab458ca770f116d28a1f22687ae620e71b59))
### Features
- add content to empty catalogs ([982fb16](https://gitlab.com/openstapps/app/commit/982fb1653b3c1253aac9366733f14c22c94d2537))
- add easy way to configure search filtering for nested properties ([2220ab2](https://gitlab.com/openstapps/app/commit/2220ab24b385188515da7c176bf9c1ac72651fd9))
- dark theme ([e75a466](https://gitlab.com/openstapps/app/commit/e75a46633ca3685d6044eb9a8b2fd670f2cd030f))
- dashboard search rework ([8c30a47](https://gitlab.com/openstapps/app/commit/8c30a47706f07eb222fac47ad7fed61f9044328a))
- implement custom cdk virtual scroll behavior ([968cb72](https://gitlab.com/openstapps/app/commit/968cb729575c529fd6d1d35da1b50a8689057c46))
- optional logout from identity provider ([8cd2d77](https://gitlab.com/openstapps/app/commit/8cd2d777ab3a67b1ab24f03aa50a3ff73550c3d2)), closes [#372](https://gitlab.com/openstapps/app/issues/372)
- revamp dashboard mensa section ([33a74d9](https://gitlab.com/openstapps/app/commit/33a74d96ae92137f53a775e90bff99e5f2d41f6a))
- rework settings page design ([2f1298c](https://gitlab.com/openstapps/app/commit/2f1298c9d7df25d2a16576245ea62c1b6094e507))
- show in-place in date series modal, resolves [#385](https://gitlab.com/openstapps/app/issues/385) ([22e70ae](https://gitlab.com/openstapps/app/commit/22e70ae92b35578b559e6644dccb8d4bfd06af1e)), closes [#386](https://gitlab.com/openstapps/app/issues/386) [#388](https://gitlab.com/openstapps/app/issues/388)
- transition to full sidebar at xl instead of lg ([cc939f3](https://gitlab.com/openstapps/app/commit/cc939f38873833b7cc0260525a2ecd536f27bfa5))
## [2.0.1](https://gitlab.com/openstapps/app/compare/v2.0.0...v2.0.1) (2023-02-13)
### Bug Fixes
- assessment segments can become unreadable ([939fb6e](https://gitlab.com/openstapps/app/commit/939fb6ef0f11b40cb71fbe61da90f50b1f75c3f7))
- login button not easily found ([11d1ac3](https://gitlab.com/openstapps/app/commit/11d1ac3f7ce27c2822ea8f839df3f3dffbd6c020))
- remove misleading assessment calculations ([aefae33](https://gitlab.com/openstapps/app/commit/aefae33d5c9fa9ee3efe346e45429aca79ae3c48))
- right-align add event detail chip ([1eee652](https://gitlab.com/openstapps/app/commit/1eee652533c6b8f613ce09df9c3421f89209419a))
### Features
- offline notice ([9b4caf5](https://gitlab.com/openstapps/app/commit/9b4caf526ffb53ec8d8885342323fcc52fd9fc09))
- separate prettier from eslint ([a88d000](https://gitlab.com/openstapps/app/commit/a88d000ccd6cbdeb5fbb07d209f2491023f9d76c))
# [2.0.0](https://gitlab.com/openstapps/app/compare/v0.0.1...v2.0.0) (2023-01-11)
### Bug Fixes
- daia_url missing in environment configs ([863a3ff](https://gitlab.com/openstapps/app/commit/863a3ffd488425e3313ab9b812c4b6d50c68a244))
- 404 on all surge links ([f1b4930](https://gitlab.com/openstapps/app/commit/f1b4930a3068e73aee20b4c3d71dac551ab60c35))
- add areaServed to person detail ([488150f](https://gitlab.com/openstapps/app/commit/488150f7f5558c05c1ec8a71afcb9f9a37e68a37))
- add event popover expanding beyond screen width ([046a95b](https://gitlab.com/openstapps/app/commit/046a95ba1dca3f5ded7e5555d3167f52f95be107))
- add location info to dates from timetable ([92adb9d](https://gitlab.com/openstapps/app/commit/92adb9dd2db18027dcc327433027e28c81ecbd4b)), closes [#344](https://gitlab.com/openstapps/app/issues/344)
- add missing profile translation ([cdb6ac4](https://gitlab.com/openstapps/app/commit/cdb6ac4084f8704d7f2336387a837b86f78c062b))
- add nav button to schedule page ([e628f39](https://gitlab.com/openstapps/app/commit/e628f396e22e51da2c9f2489fe89e42ccf474e2b))
- add openingHours config and catch its errors ([6125d43](https://gitlab.com/openstapps/app/commit/6125d43e8c18f2bf2afda67c0ff72e00d98ab34f))
- add PKCE parameters for PAIA auth ([f3e83bf](https://gitlab.com/openstapps/app/commit/f3e83bfcc88423f0935a060ccd0bf6198da58351))
- address late init from ionic components ([0bce9e5](https://gitlab.com/openstapps/app/commit/0bce9e5452fc5d05123756348dc30308de675bfa))
- adjust code to overcome the breaking changes (ionic 4 to 5) ([f779042](https://gitlab.com/openstapps/app/commit/f7790426cd2da4a6b33e2aa73783943f45b3de02)), closes [#70](https://gitlab.com/openstapps/app/issues/70)
- adjust hebis catalog templates ([83d9a4a](https://gitlab.com/openstapps/app/commit/83d9a4a8b8fe5b8687c72a717b3a2964524006e0))
- adjust library account user info ([bafabb1](https://gitlab.com/openstapps/app/commit/bafabb1d4ec299e2bea43cd4b8442ef33be2329a)), closes [#331](https://gitlab.com/openstapps/app/issues/331)
- adjust npm docker scripts and typos ([82bb15b](https://gitlab.com/openstapps/app/commit/82bb15b863e2d2e4df20244fda2f2e0d049ff43f))
- angular1 ng-if leftover ([25434d5](https://gitlab.com/openstapps/app/commit/25434d54e3800fd72a6c5d9188fb11f441d73aa9))
- assign navigation app name dynamically ([90b9733](https://gitlab.com/openstapps/app/commit/90b97339d3948b0864f634519416fe4a458b459f))
- background fetch crashing android app ([3316ad9](https://gitlab.com/openstapps/app/commit/3316ad9169ed2b29a2755405589213f824aec9d1))
- calculating SCDateSeries for next unit view ([82ba5f8](https://gitlab.com/openstapps/app/commit/82ba5f81211fb10cc5fde04991856567c4ac9680))
- canteen module layout ([b89f5c4](https://gitlab.com/openstapps/app/commit/b89f5c4edd1ed14f7529edc4e4ea54f9d98fda7c))
- canteen module layout ([6f7c680](https://gitlab.com/openstapps/app/commit/6f7c680ed89f027d863ebc02f5b24895d84f32e4))
- cards not having rounded corners in safari ([8a04a43](https://gitlab.com/openstapps/app/commit/8a04a439032920ead799f8b7483f29b896797c37))
- catch error `Setting "language" not provided` ([584878d](https://gitlab.com/openstapps/app/commit/584878d9503b8406b6ee7ec69dde5b8b3c4fd954))
- CI stage/job setup ([ae429ca](https://gitlab.com/openstapps/app/commit/ae429ca5fb3b5f10cad377d37b251806b3dabf6c))
- config.get issue by updating ionic ([9386351](https://gitlab.com/openstapps/app/commit/93863510fac32ed5b887011175a4807df3f522b8))
- **config:** fix catch ConfigFetchError in getValue ([161da63](https://gitlab.com/openstapps/app/commit/161da630ea59f6205ee53dece950972d4f36ada5)), closes [#46](https://gitlab.com/openstapps/app/issues/46)
- correct data path color ([0d755bc](https://gitlab.com/openstapps/app/commit/0d755bcbd3d6fea59a4f7a59981fd28844ff90d5))
- correct html whitespace handling for icon detection ([9bc3642](https://gitlab.com/openstapps/app/commit/9bc3642990b687dd524470fd26df80351aa85f1e))
- daia availability ([13cee2d](https://gitlab.com/openstapps/app/commit/13cee2d4261c7301c1c763446ae44dcdd005172d))
- dashboard next unit structural directive causing animation issues ([c8f6a27](https://gitlab.com/openstapps/app/commit/c8f6a27c571e51bcc0ac0120968e6bc9a20c8dd7))
- **data:** fix and adjust detail test using translation ([478f49a](https://gitlab.com/openstapps/app/commit/478f49a8744211b3c9458b3dadc791d62a46ae46)), closes [#50](https://gitlab.com/openstapps/app/issues/50)
- **data:** fix template and other consistency issues ([c3bc227](https://gitlab.com/openstapps/app/commit/c3bc227a3ca4a295b2b31bfe7fd34830b33c9f05))
- detail page when dish of mensa selected ([e5c2270](https://gitlab.com/openstapps/app/commit/e5c227073a183a1c562e17f3fe14a51048d01637)), closes [#140](https://gitlab.com/openstapps/app/issues/140)
- download events should respect selection ([28fbfef](https://gitlab.com/openstapps/app/commit/28fbfef18cc3b457f7020a70157ea7a4fff345d0))
- enable background fetch on iOS ([a1592f8](https://gitlab.com/openstapps/app/commit/a1592f84cc48f7cae8c55ef806cddbe806034bb5))
- enable overflow for day labels in schedule ([1195c5f](https://gitlab.com/openstapps/app/commit/1195c5ffc8cea07f1e224d92a7fb25aa5858bf0a))
- encode URI parameter to proxy URI ([bc13cc5](https://gitlab.com/openstapps/app/commit/bc13cc5e1fe6144fe0a0e53c0748a154844a5c29)), closes [#326](https://gitlab.com/openstapps/app/issues/326)
- extend landing period button not working on android ([0caa69c](https://gitlab.com/openstapps/app/commit/0caa69c28cbb2f962b70a1da13659739c1c6dd3e)), closes [#333](https://gitlab.com/openstapps/app/issues/333)
- failing native http requests with body ([fed4f20](https://gitlab.com/openstapps/app/commit/fed4f20c3cf43221512f3d2b6dd3c3fe7a4cf43a))
- feedback not allowing valid emails ([cf74c8e](https://gitlab.com/openstapps/app/commit/cf74c8e19f8bd34a31d5af931781e84be2c04dea)), closes [#349](https://gitlab.com/openstapps/app/issues/349)
- fix issues found by ng build for production ([a503811](https://gitlab.com/openstapps/app/commit/a503811c1cfcf909571af766ccd884856aad8ec9)), closes [#48](https://gitlab.com/openstapps/app/issues/48)
- fix various typos ([ad0dae4](https://gitlab.com/openstapps/app/commit/ad0dae46ff04d28551d2ece950d9a4d3442541d2))
- fixate webdriver to match used chrome version ([24dbb42](https://gitlab.com/openstapps/app/commit/24dbb42b345458b7dbdd17b2759824b3b68cb0e4))
- generate library online links properly ([9854541](https://gitlab.com/openstapps/app/commit/9854541a0c062c31bce167673586dccc8af81785)), closes [#340](https://gitlab.com/openstapps/app/issues/340)
- handle prices as an optional property ([9e71efc](https://gitlab.com/openstapps/app/commit/9e71efca9f7b1086db26f580192d6b349bdcb964)), closes [#219](https://gitlab.com/openstapps/app/issues/219)
- header logo changing size on ios navigate ([38f0a30](https://gitlab.com/openstapps/app/commit/38f0a300769a5b7cda35af0927c17099f93981b8))
- ignore null-island location ([d3188f5](https://gitlab.com/openstapps/app/commit/d3188f50905d610097de6c90bc58e6373d30e0dc)), closes [#149](https://gitlab.com/openstapps/app/issues/149)
- item not available in offer template ([7b402d6](https://gitlab.com/openstapps/app/commit/7b402d61c30aed81a5671d778a38c8393a5bc7c8)), closes [#110](https://gitlab.com/openstapps/app/issues/110)
- library account missing ready for pickup ([e504d8c](https://gitlab.com/openstapps/app/commit/e504d8cf6dd1c12fcb8f6a315527337313662385)), closes [#330](https://gitlab.com/openstapps/app/issues/330)
- library fines should load item title only if needed ([cbb949e](https://gitlab.com/openstapps/app/commit/cbb949e3977a5821e6bd1b654dec66a82e4d8c81)), closes [#342](https://gitlab.com/openstapps/app/issues/342)
- links of timetable tabs ([837c69b](https://gitlab.com/openstapps/app/commit/837c69bb21c92a91259051d5680e1073b390fc0e)), closes [#144](https://gitlab.com/openstapps/app/issues/144)
- logged out button not showing on profile ([ebdc14d](https://gitlab.com/openstapps/app/commit/ebdc14d3c398ac7564c077757c564f4e414fe244)), closes [#239](https://gitlab.com/openstapps/app/issues/239)
- make action chips react to changes of their item ([c0d0b1b](https://gitlab.com/openstapps/app/commit/c0d0b1bd9934e8d9e23f47825cae6a5d8ea2f38a))
- make keyboard dismissable on mobile devices ([b2cc1fd](https://gitlab.com/openstapps/app/commit/b2cc1fd91fc5bd66c994dcbe10771a22d91a1b3e))
- map search on iOS ([7d449b4](https://gitlab.com/openstapps/app/commit/7d449b43d0d86825f711848110ac6f044084eba0)), closes [#148](https://gitlab.com/openstapps/app/issues/148)
- map widget not loading tiles properly ([09aa7bb](https://gitlab.com/openstapps/app/commit/09aa7bb5c59e8d167fa91c69745f5f80229094d7)), closes [#127](https://gitlab.com/openstapps/app/issues/127)
- missing init (config setup) in default auth service ([f16e539](https://gitlab.com/openstapps/app/commit/f16e5394cce5a8019f4dfe367e5e0a2f0cca4ce2)), closes [#227](https://gitlab.com/openstapps/app/issues/227)
- missing translations ([30d801a](https://gitlab.com/openstapps/app/commit/30d801a3b419b382d40d96dda995bd35e493523d))
- modals not reacting after several uses ([f39c29f](https://gitlab.com/openstapps/app/commit/f39c29f10bc05ab986b74dfa8a8648df5fb307b4))
- news module not scrollable on large screens ([44b6a4a](https://gitlab.com/openstapps/app/commit/44b6a4aea008ca6c89f6cb289467429fb3f8c1fa))
- ngx-translate defaultLanguage not set ([581a5b2](https://gitlab.com/openstapps/app/commit/581a5b2e55ceda99cf7c41200366c3c5e09f1c63))
- omit sync native calendar when no uuids ([c9720dc](https://gitlab.com/openstapps/app/commit/c9720dc104cce78ae1a422d5efed7b8a58946836)), closes [#177](https://gitlab.com/openstapps/app/issues/177)
- opening hours pipe refreshing too often ([95e1734](https://gitlab.com/openstapps/app/commit/95e1734d26b175d8d1156abb10863155fed89ec0))
- overhaul auth to avoid issues ([99e8d6c](https://gitlab.com/openstapps/app/commit/99e8d6c9bcbc68b639b035af36bc05de0237b1f9)), closes [#336](https://gitlab.com/openstapps/app/issues/336)
- overview search bar scrolling behind header ([a037090](https://gitlab.com/openstapps/app/commit/a037090eec06e867a703b88a43620a74770287fe))
- performance degradation when scrolling ([f0a45d1](https://gitlab.com/openstapps/app/commit/f0a45d1b8eb2b33a6c68b94ed7f96f81db3a728b))
- person list should use long-inline-text ([8b2b853](https://gitlab.com/openstapps/app/commit/8b2b853942ac76904ff49d940dfef625b0397150))
- prevent multiple heavy setting inits ([f772637](https://gitlab.com/openstapps/app/commit/f7726378f443d9809a6174411d25a811e1d0b5e9))
- prevent opening invalid links ([fdee2db](https://gitlab.com/openstapps/app/commit/fdee2db8a42b8f6c99c4a72b3104ae0ba1a41c5a)), closes [#328](https://gitlab.com/openstapps/app/issues/328)
- profile module items show click effect on scroll ([8b2f2c0](https://gitlab.com/openstapps/app/commit/8b2f2c063c65278580d9469b00d9e67f01caaffb))
- recurring schedule offset and event limit ([9c6b513](https://gitlab.com/openstapps/app/commit/9c6b5131cd4cadb2572769d368b346054f19de1c))
- refresh token not used by default auth provider ([15ccbe4](https://gitlab.com/openstapps/app/commit/15ccbe4c1879417f2fc5849204fa9f6bdcce87fc)), closes [#311](https://gitlab.com/openstapps/app/issues/311)
- remove "extend landing" button when renewal not possible ([f60a228](https://gitlab.com/openstapps/app/commit/f60a22839200019a38586a14a0cce32e2c0029e7)), closes [#338](https://gitlab.com/openstapps/app/issues/338)
- remove item before adding it to secure storage ([ec511fb](https://gitlab.com/openstapps/app/commit/ec511fb8f40219e2559b08c62bd915d773d2a36f))
- schedule navigation bar layout ([e7d5f83](https://gitlab.com/openstapps/app/commit/e7d5f83100f43564b55249909a6658e583e3a9b2))
- set android status bar color correctly ([b38a969](https://gitlab.com/openstapps/app/commit/b38a96996a10e4e43ff1b06ecd2235a0e3bdfa1c))
- setting of default language ([ccf8b1a](https://gitlab.com/openstapps/app/commit/ccf8b1a5cc9fe834ba3e04a1ed67b2d082004403))
- show nothing for empty array properties ([1c56c89](https://gitlab.com/openstapps/app/commit/1c56c891e15b034826b5d6b2b35141fc872c922d)), closes [#154](https://gitlab.com/openstapps/app/issues/154)
- single map place width ([88684f0](https://gitlab.com/openstapps/app/commit/88684f068ab130f43e520c87f1d7383e0ae43944)), closes [#147](https://gitlab.com/openstapps/app/issues/147)
- some android devices don't support implied css animation units ([54cc883](https://gitlab.com/openstapps/app/commit/54cc8838aefe8f8c2d25d9228a136ef727a08230))
- status bar being black on Android 13 devices ([feee9e8](https://gitlab.com/openstapps/app/commit/feee9e8db90e66cf2346f7c5cc24f075eb70676c))
- suppress router event logs in console ([28caaf1](https://gitlab.com/openstapps/app/commit/28caaf1d21f7961b678cf339a712abf860ade5e7)), closes [#207](https://gitlab.com/openstapps/app/issues/207)
- swap missing icon after ionic update ([643b6c9](https://gitlab.com/openstapps/app/commit/643b6c967f3268cb305a24d614c3bc91275b0ac3))
- temporary disable flaky ui test ([6b9b1fa](https://gitlab.com/openstapps/app/commit/6b9b1fa8548d5c5fca04b2c1d63e893de39278a2))
- timetable dates cannot be removed ([9242438](https://gitlab.com/openstapps/app/commit/924243813207fa791d3c4938f8653a999b6382ff))
- translate back button title ([b8db0f3](https://gitlab.com/openstapps/app/commit/b8db0f3e70a46f2b493e183a244cb29d1954c257))
- translations ([5e1a902](https://gitlab.com/openstapps/app/commit/5e1a902d4c0d2345f21500fba5394c1907e04eb8))
- typo in translation ([7928534](https://gitlab.com/openstapps/app/commit/7928534d88a26db28b098bbceb47bc1103022a57))
- update core and apply stricter tslint rules ([911492d](https://gitlab.com/openstapps/app/commit/911492d064ff0280dd6626244cd8038cbfc0f408))
- use HashLocationStrategy for routes ([9d68212](https://gitlab.com/openstapps/app/commit/9d682125db55c87cab2b33c7633bfa133d2fc5a9)), closes [#54](https://gitlab.com/openstapps/app/issues/54)
- use localized date and time ([6ca0b97](https://gitlab.com/openstapps/app/commit/6ca0b9763761c5204a757a243056a087c5f35fd9))
- use stapps core version to compare with backends' core version ([66b8720](https://gitlab.com/openstapps/app/commit/66b8720da0f264824a396f2d9e598b0e48c8e3d1)), closes [#77](https://gitlab.com/openstapps/app/issues/77)
- user info card ([998edcb](https://gitlab.com/openstapps/app/commit/998edcb5cdfb588c2986f466f4a2951f172a8bb4)), closes [#305](https://gitlab.com/openstapps/app/issues/305)
### Features
- add "no results" screen to search ([c75ca68](https://gitlab.com/openstapps/app/commit/c75ca68c440a20e197213ecbb47d05fc293afd9c))
- add about module ([d420008](https://gitlab.com/openstapps/app/commit/d42000892694f4a3b29fa623c43fb45f8ba54687))
- add action chips to search results ([67fb4a4](https://gitlab.com/openstapps/app/commit/67fb4a43c95043caba50d43ace2ab276f3319b81))
- add auth support (default and paia) ([b5f239e](https://gitlab.com/openstapps/app/commit/b5f239ea4edebd0d27b1cdaad4a830998ce2f681))
- add backend toggle ([c1d3330](https://gitlab.com/openstapps/app/commit/c1d33303aa11da3b3e150c6717d77ef484a16ac1))
- add basic templates for data list items ([3e697b1](https://gitlab.com/openstapps/app/commit/3e697b17b4448c15781d0f6dd55577b638e9d974))
- add catalog module ([03084b1](https://gitlab.com/openstapps/app/commit/03084b1c966de98b3723d0bee2b2475589393c59))
- add ConfigModule and FakeBackendInterceptor ([406f400](https://gitlab.com/openstapps/app/commit/406f40055567bfde4ec5edf26cff942411bd073e)), closes [#34](https://gitlab.com/openstapps/app/issues/34) [#37](https://gitlab.com/openstapps/app/issues/37)
- add detail view for news ([2566a71](https://gitlab.com/openstapps/app/commit/2566a71a81a3408dbb16b97d3a453d95e25d1f00))
- add duration pipe with frequency capabilites ([49a1758](https://gitlab.com/openstapps/app/commit/49a1758da358958ffe590700c19aaf90ec748ee5))
- add favorites support ([e9452d6](https://gitlab.com/openstapps/app/commit/e9452d6520c34f6513623c16e291dc23d6ea9757))
- add feedback module ([867f9e9](https://gitlab.com/openstapps/app/commit/867f9e9b832e3bd54c04801fef63a11543e8f3dd))
- add filter chips for news ([5435f85](https://gitlab.com/openstapps/app/commit/5435f85cc43dc3baa774a5008d2920ac7b3783f6))
- add formatting pipes basted on Intl ([4b932af](https://gitlab.com/openstapps/app/commit/4b932af1c07e1af4369414667a987d31181c073c))
- add HeBIS HDS search ([9a3241c](https://gitlab.com/openstapps/app/commit/9a3241c42ab59e15c0084178f76dc4a2450a2bb8))
- add library account screens ([080e6fa](https://gitlab.com/openstapps/app/commit/080e6fa3e8c18e9608d7fa2efc95e4fd65c43a15))
- add library action confirmations ([42b860e](https://gitlab.com/openstapps/app/commit/42b860e41793fc3983a39237a4f7128416485fae)), closes [#334](https://gitlab.com/openstapps/app/issues/334)
- add logger ([a0c798f](https://gitlab.com/openstapps/app/commit/a0c798f765d87c5eebcbed65b70f3b05f285d0ce))
- add map module ([c1c9a92](https://gitlab.com/openstapps/app/commit/c1c9a92ec900403218b887fdebfe5132b232e1e0))
- add new font and new icons ([915fd72](https://gitlab.com/openstapps/app/commit/915fd72bd4bfed16e15fcc3c57879db0ec0379e2))
- add not found screen ([e3d9ef4](https://gitlab.com/openstapps/app/commit/e3d9ef40ccd626c81c67ea2d790eecbe6e025780))
- add permission check for geoLocation setting ([d5fa2fd](https://gitlab.com/openstapps/app/commit/d5fa2fd9a578d48cd2513eeb1380f1d2bc4d3754))
- add service and pipe for core translator ([4565600](https://gitlab.com/openstapps/app/commit/456560026cc9550a10a9f42657d942122be34d53))
- apply new layout overhaul ([7bbdba5](https://gitlab.com/openstapps/app/commit/7bbdba5c0b886e2789d2a603c4be627dfd16b60e))
- assessment tree view ([0b037f9](https://gitlab.com/openstapps/app/commit/0b037f96e634b412fbaaee24747df08afdc0e565))
- assessments module ([e68d1b7](https://gitlab.com/openstapps/app/commit/e68d1b73f94b36abcefe9b2bf42e98de00b69188))
- calendar plugin ([a57c302](https://gitlab.com/openstapps/app/commit/a57c3029df61ac3157c856744380a85dc001abc6))
- dashboard ui tests ([9f8ab5c](https://gitlab.com/openstapps/app/commit/9f8ab5c7a15a918f7bd05423f0a43f22a33d9228))
- **data:** add basic methods of data provider ([ffe05e4](https://gitlab.com/openstapps/app/commit/ffe05e4548fc399183ef651047cb02a3cdc80c67)), closes [#1](https://gitlab.com/openstapps/app/issues/1)
- **data:** add data detail templates ([5855acc](https://gitlab.com/openstapps/app/commit/5855accc169579d87f5779fd602d4f00f2b479a1))
- **data:** add method that checks if data item has been saved ([017fc67](https://gitlab.com/openstapps/app/commit/017fc67765bdb75542045830fb85f8eb4b899485))
- **data:** implement basic facets handling ([b6f92a7](https://gitlab.com/openstapps/app/commit/b6f92a74494e1a39d5c828d593eb70c7002bb0f6)), closes [#1](https://gitlab.com/openstapps/app/issues/1)
- **data:** show skeleton screens before data is loaded ([e1039aa](https://gitlab.com/openstapps/app/commit/e1039aa2260a0e9d8ccca5a18ded480bf2f12530)), closes [#4](https://gitlab.com/openstapps/app/issues/4)
- **data:** use data provider for list and detail view ([7caaa18](https://gitlab.com/openstapps/app/commit/7caaa18b7eb94ed8e4ff4e54d760ba1dba3ae3be))
- **data:** use general template for all offers ([58960a2](https://gitlab.com/openstapps/app/commit/58960a29ea0cd73f26a43186b41f5b53864810e0))
- display availability and item data for library items ([d571b1d](https://gitlab.com/openstapps/app/commit/d571b1dbe59f8e2270d88dd050b94283bf0a7056))
- dynamic news page rows ([848d257](https://gitlab.com/openstapps/app/commit/848d2574c7046d6f84b91e32aa83ca57fca8ad2e))
- expandable accordion in grades module ([3f81afd](https://gitlab.com/openstapps/app/commit/3f81afda82cf87bb393fbb3b71d27eee77ae42d9))
- get tab navigation items from config ([c3130a3](https://gitlab.com/openstapps/app/commit/c3130a392a53c9ec3657e24a53ed0b3333ba162b))
- Implement variable for styling ([8ecf347](https://gitlab.com/openstapps/app/commit/8ecf347c9a8abac9347dca45e99ebe35eca8f252))
- include font licenses in the licenses section ([82479f4](https://gitlab.com/openstapps/app/commit/82479f463cbee834507a0c8faf895a729631eb06))
- Ionic v6 breadcrumbs in catalog module ([7b491ed](https://gitlab.com/openstapps/app/commit/7b491ed3bb5466a845493bd8ea0bbb836a4f03d2))
- lazy load all news ([e48134e](https://gitlab.com/openstapps/app/commit/e48134eddcd1ca4d5ec5dbf910571e33a3311ba1))
- **menu:** add context menu ([1dbf451](https://gitlab.com/openstapps/app/commit/1dbf4515fe57cc8250a7fa2213ced682e3e5e0fc)), closes [#3](https://gitlab.com/openstapps/app/issues/3)
- navigation rail ([6b08af6](https://gitlab.com/openstapps/app/commit/6b08af6a746bf12005d3297ec97c130e84477615))
- news module ([22cd0af](https://gitlab.com/openstapps/app/commit/22cd0af1bf92a4576316f5510c22f012e085a237))
- profile page sections ([e395e9d](https://gitlab.com/openstapps/app/commit/e395e9d270f41bd4f6e5ecd88e438a28dde92465)), closes [#233](https://gitlab.com/openstapps/app/issues/233) [#261](https://gitlab.com/openstapps/app/issues/261) [#267](https://gitlab.com/openstapps/app/issues/267)
- refresh on pull for news module ([1f3d9ad](https://gitlab.com/openstapps/app/commit/1f3d9ad5f0c0557add2dcf242cf0b7dd7685bb1b))
- resume at origin path after login ([a5e5a5b](https://gitlab.com/openstapps/app/commit/a5e5a5b40799e7505557f7ebd764b9c563be0f4b)), closes [#168](https://gitlab.com/openstapps/app/issues/168)
- schedule layout ([e416590](https://gitlab.com/openstapps/app/commit/e4165901bb5efa6b38e397cdf5d66138e396d7f2))
- scroll schedule cursor into view ([bc4c3d7](https://gitlab.com/openstapps/app/commit/bc4c3d78dbd906243dcddac4db9ac8ccaca79056))
- search url query param handling ([f349bd7](https://gitlab.com/openstapps/app/commit/f349bd72335c47d292d0a007b1a4ce7f5c694a61))
- seperate dishes by menu sections ([400c6b8](https://gitlab.com/openstapps/app/commit/400c6b8d8c5300035862186096e38883f781d297))
- show availability in offers ([5fdef95](https://gitlab.com/openstapps/app/commit/5fdef95c065e7b467a13045adbb56a641cb2dc12))
- show feedback when map search yields no results ([c54ea86](https://gitlab.com/openstapps/app/commit/c54ea867bd64af187446dbf8dc03967aae600961))
- show menu for multiple days for canteens and cafes ([3c079cd](https://gitlab.com/openstapps/app/commit/3c079cd189e3b75d3b203bd46ab57378f99e88cc)), closes [#19](https://gitlab.com/openstapps/app/issues/19) [#79](https://gitlab.com/openstapps/app/issues/79)
- **storage:** add search using regex ([86b9bff](https://gitlab.com/openstapps/app/commit/86b9bff09a51b17151efa5ec322cddcc50a54cb2))
- **storage:** support deletion of multiple entries ([63266f5](https://gitlab.com/openstapps/app/commit/63266f588f6ddb2476e2cea4bab42a3864f77470))
- **storage:** support search using a string ([4334cad](https://gitlab.com/openstapps/app/commit/4334cad68c7d0abb7443e245b6eb983804547925))
- support deep links ([2e3f668](https://gitlab.com/openstapps/app/commit/2e3f6684ef5fbac8e4fb127c536b2b438399ce37))
- support overlapping timetable dates ([93c37b2](https://gitlab.com/openstapps/app/commit/93c37b26cca7764dea66fb12c0e53acc8fd78d2b))
- tab navigation bar animations and state ([7ecba0b](https://gitlab.com/openstapps/app/commit/7ecba0b7819ae5a7ab32d86f6049de0ab6c68e55))
- timetable module - schedule and calendar ([d8ede00](https://gitlab.com/openstapps/app/commit/d8ede006dfbd613dfbc752eb6de80db2e7e84531))
- turn on oauth2 state check for PAIA ([5bd0b50](https://gitlab.com/openstapps/app/commit/5bd0b50816973548a0a4aa5dbed3f2d0902590dd)), closes [#172](https://gitlab.com/openstapps/app/issues/172)
- use http interceptor for backendless development ([2558163](https://gitlab.com/openstapps/app/commit/2558163ad6c3038445a79e6338f1827cb1e6510e)), closes [#37](https://gitlab.com/openstapps/app/issues/37)
- use school-neutral news image fallback via css ([bb94c71](https://gitlab.com/openstapps/app/commit/bb94c71761ca5d3bf6639476ba0c143678cfabbd))
- webpack bundle analyzer ([552911c](https://gitlab.com/openstapps/app/commit/552911cfcfecec8d9be8b8bbb155b597d1f70fa2))
## [0.0.1](https://gitlab.com/openstapps/app/compare/8b23159e678773b08252bc5826510de2a302fa1d...v0.0.1) (2018-12-11)
### Features
- add the app ([8b23159](https://gitlab.com/openstapps/app/commit/8b23159e678773b08252bc5826510de2a302fa1d))

View File

@@ -29,7 +29,10 @@
"TITLE_COPIED": "In die Zwischenablage kopiert" "TITLE_COPIED": "In die Zwischenablage kopiert"
}, },
"about": { "about": {
"VERSION_INFO": "{{name}} {{version}} basierend auf StApps {{stappsVersion}}" "VERSION_INFO": "{{name}} {{version}} basierend auf StApps {{stappsVersion}}",
"CHANGELOG": "Versionshistorie",
"TECHNICAL_CHANGELOG": "StApps Basis Versionshistorie",
"TECHNICAL_CHANGELOG_DISCLAIMER": "Die StApps Basis ist anders versioniert als die App. Versionsnummern zwischen StApps und App weichen daher ab."
}, },
"releaseNotes": { "releaseNotes": {
"TITLE_UPDATED": "Deine App wurde aktualisiert!" "TITLE_UPDATED": "Deine App wurde aktualisiert!"
@@ -336,11 +339,15 @@
"title": "Titel", "title": "Titel",
"about": "Mehr Informationen", "about": "Mehr Informationen",
"label": "Signatur", "label": "Signatur",
"starttime": "Vorgemerkt am",
"endtime": "Abzuholen bis", "endtime": "Abzuholen bis",
"storage": "Abholbereit" "storage": "Abholtheke",
"queue": "Position in der Warteschlange"
}, },
"holds": "Bestellungen", "holds": "Bestellungen",
"reservations": "Vormerkungen" "reservations": "Vormerkungen",
"ordered": "Bestellt",
"ready": "Abholbereit"
}, },
"checked_out": { "checked_out": {
"title": "Deine Ausleihen", "title": "Deine Ausleihen",
@@ -348,7 +355,7 @@
"title": "Titel", "title": "Titel",
"about": "Mehr Informationen", "about": "Mehr Informationen",
"label": "Signatur", "label": "Signatur",
"endtime": "Leihfristende", "duedate": "Leihfristende",
"renewals": "Verlängerungen" "renewals": "Verlängerungen"
} }
}, },

View File

@@ -29,7 +29,10 @@
"TITLE_COPIED": "Copied to clipboard" "TITLE_COPIED": "Copied to clipboard"
}, },
"about": { "about": {
"VERSION_INFO": "{{name}} {{version}} based on StApps {{stappsVersion}}" "VERSION_INFO": "{{name}} {{version}} based on StApps {{stappsVersion}}",
"CHANGELOG": "Changelog",
"TECHNICAL_CHANGELOG": "StApps Base Changelog",
"TECHNICAL_CHANGELOG_DISCLAIMER": "The StApps base is versioned separately from the app. Version numbers will not match those of the app itself."
}, },
"releaseNotes": { "releaseNotes": {
"TITLE_UPDATED": "Your app was updated!" "TITLE_UPDATED": "Your app was updated!"
@@ -336,11 +339,15 @@
"title": "Title", "title": "Title",
"about": "More information", "about": "More information",
"label": "Shelfmark", "label": "Shelfmark",
"starttime": "Reserved on",
"endtime": "Available for pickup until", "endtime": "Available for pickup until",
"storage": "Available for pickup" "storage": "Pickup counter",
"queue": "Position in the queue"
}, },
"holds": "orders", "holds": "orders",
"reservations": "reservations" "reservations": "reservations",
"ordered": "Ordered",
"ready": "Ready for pickup"
}, },
"checked_out": { "checked_out": {
"title": "checked out items", "title": "checked out items",
@@ -348,7 +355,7 @@
"title": "Title", "title": "Title",
"about": "More information", "about": "More information",
"label": "Label", "label": "Label",
"endtime": "Due date", "duedate": "Due date",
"renewals": "Renewals" "renewals": "Renewals"
} }
}, },

View File

@@ -21,7 +21,7 @@ export const environment = {
backend_url: 'https://mobile.server.uni-frankfurt.de', backend_url: 'https://mobile.server.uni-frankfurt.de',
app_host: 'mobile.app.uni-frankfurt.de', app_host: 'mobile.app.uni-frankfurt.de',
custom_url_scheme: 'de.anyschool.app', custom_url_scheme: 'de.anyschool.app',
backend_version: '3.1.0', backend_version: '3.3.0',
production: true, production: true,
}; };

View File

@@ -21,7 +21,7 @@ export const environment = {
backend_url: 'https://mobile.server.uni-frankfurt.de', backend_url: 'https://mobile.server.uni-frankfurt.de',
app_host: 'mobile.app.uni-frankfurt.de', app_host: 'mobile.app.uni-frankfurt.de',
custom_url_scheme: 'de.anyschool.app', custom_url_scheme: 'de.anyschool.app',
backend_version: '3.1.0', backend_version: '999.0.0',
production: false, production: false,
}; };

View File

@@ -18,15 +18,15 @@
@import './dark'; @import './dark';
@function to-rgb($color) { @function to-rgb($color) {
@return red($color) + ',' + green($color) + ',' + blue($color); @return color.red($color) + ',' + color.green($color) + ',' + color.blue($color);
} }
@function to-shade($color) { @function to-shade($color) {
@return darken($color, $shade-amount); @return color.adjust($color, $lightness: -$shade-amount);
} }
@function to-tint($color) { @function to-tint($color) {
@return lighten($color, $tint-amount); @return color.adjust($color, $lightness: $tint-amount);
} }
@function to-contrast($color) { @function to-contrast($color) {
@@ -35,7 +35,11 @@
} }
@function fade($color, $amount) { @function fade($color, $amount) {
@return if(lightness($color) > $fade-threshold, darken($color, $amount), lighten($color, $amount)); @return if(
color.lightness($color) > $fade-threshold,
color.adjust($color, $lightness: -$amount),
color.adjust($color, $lightness: $amount)
);
} }
@function map-range($value, $input-min, $input-max, $output-min, $output-max) { @function map-range($value, $input-min, $input-max, $output-min, $output-max) {
@@ -44,9 +48,9 @@
@function interpolate-colors($from, $to, $progress, $progress-min: 0, $progress-max: 100) { @function interpolate-colors($from, $to, $progress, $progress-min: 0, $progress-max: 100) {
@return rgb( @return rgb(
map-range($progress, $progress-min, $progress-max, red($from), red($to)), map-range($progress, $progress-min, $progress-max, color.red($from), color.red($to)),
map-range($progress, $progress-min, $progress-max, green($from), green($to)), map-range($progress, $progress-min, $progress-max, color.green($from), color.green($to)),
map-range($progress, $progress-min, $progress-max, blue($from), blue($to)) map-range($progress, $progress-min, $progress-max, color.blue($from), color.blue($to))
); );
} }

View File

@@ -6,9 +6,9 @@ LABEL version="2.0.0" \
maintainer="Jovan Krunić <krunic@uni-frankfurt.de>" maintainer="Jovan Krunić <krunic@uni-frankfurt.de>"
### Configure versions to install ### Configure versions to install
ENV ANDROID_APIS="android-33" \ ENV ANDROID_APIS="android-34" \
ANDROID_BUILD_TOOLS_VERSION="30.0.2" \ ANDROID_BUILD_TOOLS_VERSION="34.0.0" \
NPM_VERSION="^9.0.0" \ NPM_VERSION="^10.0.0" \
IONIC_VERSION="^6.0.0" \ IONIC_VERSION="^6.0.0" \
CORDOVA_RES_VERSION="latest" \ CORDOVA_RES_VERSION="latest" \
### Configure download URLs ### Configure download URLs

View File

@@ -1,372 +1,108 @@
#!/bin/bash #!/bin/bash
# Discussion, issues and change requests at: # Logger Function
# https://github.com/nodesource/distributions log() {
# local message="$1"
# Script to install the NodeSource Node.js 18.x repo onto a local type="$2"
# Debian or Ubuntu system. local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
# local color
# Run as root or insert `sudo -E` before `bash`: local endcolor="\033[0m"
#
# curl -sL https://deb.nodesource.com/setup_18.x | bash -
# or
# wget -qO- https://deb.nodesource.com/setup_18.x | bash -
#
# CONTRIBUTIONS TO THIS SCRIPT
#
# This script is built from a template in
# https://github.com/nodesource/distributions/tree/master/deb/src
# please don't submit pull requests against the built scripts.
#
case "$type" in
"info") color="\033[38;5;79m" ;;
"success") color="\033[1;32m" ;;
"error") color="\033[1;31m" ;;
*) color="\033[1;34m" ;;
esac
export DEBIAN_FRONTEND=noninteractive echo -e "${color}${timestamp} - ${message}${endcolor}"
SCRSUFFIX="_18.x"
NODENAME="Node.js 18.x"
NODEREPO="node_18.x"
NODEPKG="nodejs"
print_status() {
echo
echo "## $1"
echo
} }
if test -t 1; then # if terminal # Error handler function
ncolors=$(which tput > /dev/null && tput colors) # supports color handle_error() {
if test -n "$ncolors" && test $ncolors -ge 8; then local exit_code=$1
termcols=$(tput cols) local error_message="$2"
bold="$(tput bold)" log "Error: $error_message (Exit Code: $exit_code)" "error"
underline="$(tput smul)" exit $exit_code
standout="$(tput smso)"
normal="$(tput sgr0)"
black="$(tput setaf 0)"
red="$(tput setaf 1)"
green="$(tput setaf 2)"
yellow="$(tput setaf 3)"
blue="$(tput setaf 4)"
magenta="$(tput setaf 5)"
cyan="$(tput setaf 6)"
white="$(tput setaf 7)"
fi
fi
print_bold() {
title="$1"
text="$2"
echo
echo "${red}================================================================================${normal}"
echo "${red}================================================================================${normal}"
echo
echo -e " ${bold}${yellow}${title}${normal}"
echo
echo -en " ${text}"
echo
echo "${red}================================================================================${normal}"
echo "${red}================================================================================${normal}"
} }
bail() { # Function to check for command availability
echo 'Error executing command, exiting' command_exists() {
exit 1 command -v "$1" &> /dev/null
} }
exec_cmd_nobail() { check_os() {
echo "+ $1" if ! [ -f "/etc/debian_version" ]; then
bash -c "$1" echo "Error: This script is only supported on Debian-based systems."
} exit 1
exec_cmd() {
exec_cmd_nobail "$1" || bail
}
node_deprecation_warning() {
if [[ "X${NODENAME}" == "Xio.js 1.x" ||
"X${NODENAME}" == "Xio.js 2.x" ||
"X${NODENAME}" == "Xio.js 3.x" ||
"X${NODENAME}" == "XNode.js 0.10" ||
"X${NODENAME}" == "XNode.js 0.12" ||
"X${NODENAME}" == "XNode.js 4.x LTS Argon" ||
"X${NODENAME}" == "XNode.js 5.x" ||
"X${NODENAME}" == "XNode.js 6.x LTS Boron" ||
"X${NODENAME}" == "XNode.js 7.x" ||
"X${NODENAME}" == "XNode.js 8.x LTS Carbon" ||
"X${NODENAME}" == "XNode.js 9.x" ||
"X${NODENAME}" == "XNode.js 10.x" ||
"X${NODENAME}" == "XNode.js 11.x" ||
"X${NODENAME}" == "XNode.js 12.x" ||
"X${NODENAME}" == "XNode.js 13.x" ||
"X${NODENAME}" == "XNode.js 14.x" ||
"X${NODENAME}" == "XNode.js 15.x" ||
"X${NODENAME}" == "XNode.js 17.x" ]]; then
print_bold \
" DEPRECATION WARNING " "\
${bold}${NODENAME} is no longer actively supported!${normal}
${bold}You will not receive security or critical stability updates${normal} for this version.
You should migrate to a supported version of Node.js as soon as possible.
Use the installation script that corresponds to the version of Node.js you
wish to install. e.g.
* ${green}https://deb.nodesource.com/setup_16.x — Node.js 16 \"Gallium\"${normal}
* ${green}https://deb.nodesource.com/setup_18.x — Node.js 18 LTS \"Hydrogen\"${normal} (recommended)
* ${green}https://deb.nodesource.com/setup_19.x — Node.js 19 \"Nineteen\"${normal}
* ${green}https://deb.nodesource.com/setup_20.x — Node.js 20 \"Iron\"${normal} (current)
Please see ${bold}https://github.com/nodejs/Release${normal} for details about which
version may be appropriate for you.
The ${bold}NodeSource${normal} Node.js distributions repository contains
information both about supported versions of Node.js and supported Linux
distributions. To learn more about usage, see the repository:
${bold}https://github.com/nodesource/distributions${normal}
"
echo
echo "Continuing in 20 seconds ..."
echo
sleep 20
fi fi
} }
script_deprecation_warning() { # Function to Install the script pre-requisites
if [ "X${SCRSUFFIX}" == "X" ]; then install_pre_reqs() {
print_bold \ log "Installing pre-requisites" "info"
" SCRIPT DEPRECATION WARNING " "\
This script, located at ${bold}https://deb.nodesource.com/setup${normal}, used to
install Node.js 0.10, is deprecated and will eventually be made inactive.
You should use the script that corresponds to the version of Node.js you # Run 'apt-get update'
wish to install. e.g. if ! apt-get update -y; then
handle_error "$?" "Failed to run 'apt-get update'"
fi
* ${green}https://deb.nodesource.com/setup_16.x — Node.js 16 \"Gallium\"${normal} # Run 'apt-get install'
* ${green}https://deb.nodesource.com/setup_18.x — Node.js 18 LTS \"Hydrogen\"${normal} (recommended) if ! apt-get install -y apt-transport-https ca-certificates curl gnupg; then
* ${green}https://deb.nodesource.com/setup_19.x — Node.js 19 \"Nineteen\"${normal} handle_error "$?" "Failed to install packages"
* ${green}https://deb.nodesource.com/setup_20.x — Node.js 20 \"Iron\"${normal} (current) fi
Please see ${bold}https://github.com/nodejs/Release${normal} for details about which if ! mkdir -p /usr/share/keyrings; then
version may be appropriate for you. handle_error "$?" "Makes sure the path /usr/share/keyrings exist or run ' mkdir -p /usr/share/keyrings' with sudo"
fi
The ${bold}NodeSource${normal} Node.js Linux distributions GitHub repository contains rm -f /usr/share/keyrings/nodesource.gpg || true
information about which versions of Node.js and which Linux distributions rm -f /etc/apt/sources.list.d/nodesource.list || true
are supported and how to use the install scripts.
${bold}https://github.com/nodesource/distributions${normal}
"
echo # Run 'curl' and 'gpg'
echo "Continuing in 20 seconds (press Ctrl-C to abort) ..." if ! curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /usr/share/keyrings/nodesource.gpg; then
echo handle_error "$?" "Failed to download and import the NodeSource signing key"
sleep 20
fi fi
} }
setup() { # Function to configure the Repo
configure_repo() {
local node_version=$1
script_deprecation_warning arch=$(dpkg --print-architecture)
node_deprecation_warning if [ "$arch" != "amd64" ] && [ "$arch" != "arm64" ] && [ "$arch" != "armhf" ]; then
handle_error "1" "Unsupported architecture: $arch. Only amd64, arm64, and armhf are supported."
fi
print_status "Installing the NodeSource ${NODENAME} repo..." echo "deb [arch=$arch signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$node_version nodistro main" | tee /etc/apt/sources.list.d/nodesource.list > /dev/null
if $(uname -m | grep -Eq ^armv6); then # N|solid Config
print_status "You appear to be running on ARMv6 hardware. Unfortunately this is not currently supported by the NodeSource Linux distributions. Please use the 'linux-armv6l' binary tarballs available directly from nodejs.org for Node.js 4 and later." echo "Package: nsolid" | tee /etc/apt/preferences.d/nsolid > /dev/null
exit 1 echo "Pin: origin deb.nodesource.com" | tee -a /etc/apt/preferences.d/nsolid > /dev/null
fi echo "Pin-Priority: 600" | tee -a /etc/apt/preferences.d/nsolid > /dev/null
PRE_INSTALL_PKGS="" # Nodejs Config
echo "Package: nodejs" | tee /etc/apt/preferences.d/nodejs > /dev/null
echo "Pin: origin deb.nodesource.com" | tee -a /etc/apt/preferences.d/nodejs > /dev/null
echo "Pin-Priority: 600" | tee -a /etc/apt/preferences.d/nodejs > /dev/null
# Check that HTTPS transport is available to APT # Run 'apt-get update'
# (Check snaked from: https://get.docker.io/ubuntu/) if ! apt-get update -y; then
handle_error "$?" "Failed to run 'apt-get update'"
if [ ! -e /usr/lib/apt/methods/https ]; then else
PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} apt-transport-https" log "Repository configured successfully."
fi log "To install Node.js, run: apt-get install nodejs -y" "info"
log "You can use N|solid Runtime as a node.js alternative" "info"
if [ ! -x /usr/bin/lsb_release ]; then log "To install N|solid Runtime, run: apt-get install nsolid -y \n" "success"
PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} lsb-release"
fi
if [ ! -x /usr/bin/curl ] && [ ! -x /usr/bin/wget ]; then
PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} curl"
fi
# Used by apt-key to add new keys
if [ ! -x /usr/bin/gpg ]; then
PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} gnupg"
fi
# Populating Cache
print_status "Populating apt-get cache..."
exec_cmd 'apt-get update'
if [ "X${PRE_INSTALL_PKGS}" != "X" ]; then
print_status "Installing packages required for setup:${PRE_INSTALL_PKGS}..."
# This next command needs to be redirected to /dev/null or the script will bork
# in some environments
exec_cmd "apt-get install -y${PRE_INSTALL_PKGS} > /dev/null 2>&1"
fi
IS_PRERELEASE=$(lsb_release -d | grep 'Ubuntu .*development' >& /dev/null; echo $?)
if [[ $IS_PRERELEASE -eq 0 ]]; then
print_status "Your distribution, identified as \"$(lsb_release -d -s)\", is a pre-release version of Ubuntu. NodeSource does not maintain official support for Ubuntu versions until they are formally released. You can try using the manual installation instructions available at https://github.com/nodesource/distributions and use the latest supported Ubuntu version name as the distribution identifier, although this is not guaranteed to work."
exit 1
fi
DISTRO=$(lsb_release -c -s)
check_alt() {
if [ "X${DISTRO}" == "X${2}" ]; then
echo
echo "## You seem to be using ${1} version ${DISTRO}."
echo "## This maps to ${3} \"${4}\"... Adjusting for you..."
DISTRO="${4}"
fi fi
} }
check_alt "Astra Linux" "orel" "Debian" "stretch" # Define Node.js version
check_alt "BOSS" "anokha" "Debian" "wheezy" NODE_VERSION="18.x"
check_alt "BOSS" "anoop" "Debian" "jessie"
check_alt "BOSS" "drishti" "Debian" "stretch"
check_alt "BOSS" "unnati" "Debian" "buster"
check_alt "BOSS" "urja" "Debian" "bullseye"
check_alt "bunsenlabs" "bunsen-hydrogen" "Debian" "jessie"
check_alt "bunsenlabs" "helium" "Debian" "stretch"
check_alt "bunsenlabs" "lithium" "Debian" "buster"
check_alt "Devuan" "jessie" "Debian" "jessie"
check_alt "Devuan" "ascii" "Debian" "stretch"
check_alt "Devuan" "beowulf" "Debian" "buster"
check_alt "Devuan" "chimaera" "Debian" "bullseye"
check_alt "Devuan" "ceres" "Debian" "sid"
check_alt "Devuan" "daedalus" "Debian" "bookworm"
check_alt "Deepin" "panda" "Debian" "sid"
check_alt "Deepin" "unstable" "Debian" "sid"
check_alt "Deepin" "stable" "Debian" "buster"
check_alt "Deepin" "apricot" "Debian" "buster"
check_alt "Deepin" "beige" "Debian" "bookworm"
check_alt "elementaryOS" "luna" "Ubuntu" "precise"
check_alt "elementaryOS" "freya" "Ubuntu" "trusty"
check_alt "elementaryOS" "loki" "Ubuntu" "xenial"
check_alt "elementaryOS" "juno" "Ubuntu" "bionic"
check_alt "elementaryOS" "hera" "Ubuntu" "bionic"
check_alt "elementaryOS" "odin" "Ubuntu" "focal"
check_alt "elementaryOS" "jolnir" "Ubuntu" "focal"
check_alt "elementaryOS" "horus" "Ubuntu" "jammy"
check_alt "Kali" "sana" "Debian" "jessie"
check_alt "Kali" "kali-rolling" "Debian" "bullseye"
check_alt "Linux Mint" "maya" "Ubuntu" "precise"
check_alt "Linux Mint" "qiana" "Ubuntu" "trusty"
check_alt "Linux Mint" "rafaela" "Ubuntu" "trusty"
check_alt "Linux Mint" "rebecca" "Ubuntu" "trusty"
check_alt "Linux Mint" "rosa" "Ubuntu" "trusty"
check_alt "Linux Mint" "sarah" "Ubuntu" "xenial"
check_alt "Linux Mint" "serena" "Ubuntu" "xenial"
check_alt "Linux Mint" "sonya" "Ubuntu" "xenial"
check_alt "Linux Mint" "sylvia" "Ubuntu" "xenial"
check_alt "Linux Mint" "tara" "Ubuntu" "bionic"
check_alt "Linux Mint" "tessa" "Ubuntu" "bionic"
check_alt "Linux Mint" "tina" "Ubuntu" "bionic"
check_alt "Linux Mint" "tricia" "Ubuntu" "bionic"
check_alt "Linux Mint" "ulyana" "Ubuntu" "focal"
check_alt "Linux Mint" "ulyssa" "Ubuntu" "focal"
check_alt "Linux Mint" "uma" "Ubuntu" "focal"
check_alt "Linux Mint" "una" "Ubuntu" "focal"
check_alt "Linux Mint" "vanessa" "Ubuntu" "jammy"
check_alt "Linux Mint" "vera" "Ubuntu" "jammy"
check_alt "Liquid Lemur" "lemur-3" "Debian" "stretch"
check_alt "LMDE" "betsy" "Debian" "jessie"
check_alt "LMDE" "cindy" "Debian" "stretch"
check_alt "LMDE" "debbie" "Debian" "buster"
check_alt "LMDE" "elsie" "Debian" "bullseye"
check_alt "MX Linux 17" "Horizon" "Debian" "stretch"
check_alt "MX Linux 18" "Continuum" "Debian" "stretch"
check_alt "MX Linux 19" "patito feo" "Debian" "buster"
check_alt "MX Linux 21" "wildflower" "Debian" "bullseye"
check_alt "Pardus" "onyedi" "Debian" "stretch"
check_alt "Parrot" "ara" "Debian" "bullseye"
check_alt "PureOS" "green" "Debian" "sid"
check_alt "PureOS" "amber" "Debian" "buster"
check_alt "PureOS" "byzantium" "Debian" "bullseye"
check_alt "SolydXK" "solydxk-9" "Debian" "stretch"
check_alt "Sparky Linux" "Tyche" "Debian" "stretch"
check_alt "Sparky Linux" "Nibiru" "Debian" "buster"
check_alt "Sparky Linux" "Po-Tolo" "Debian" "bullseye"
check_alt "Tanglu" "chromodoris" "Debian" "jessie"
check_alt "Trisquel" "toutatis" "Ubuntu" "precise"
check_alt "Trisquel" "belenos" "Ubuntu" "trusty"
check_alt "Trisquel" "flidas" "Ubuntu" "xenial"
check_alt "Trisquel" "etiona" "Ubuntu" "bionic"
check_alt "Ubilinux" "dolcetto" "Debian" "stretch"
check_alt "Uruk GNU/Linux" "lugalbanda" "Ubuntu" "xenial"
if [ "X${DISTRO}" == "Xdebian" ]; then # Check OS
print_status "Unknown Debian-based distribution, checking /etc/debian_version..." check_os
NEWDISTRO=$([ -e /etc/debian_version ] && cut -d/ -f1 < /etc/debian_version)
if [ "X${NEWDISTRO}" == "X" ]; then
print_status "Could not determine distribution from /etc/debian_version..."
else
DISTRO=$NEWDISTRO
print_status "Found \"${DISTRO}\" in /etc/debian_version..."
fi
fi
print_status "Confirming \"${DISTRO}\" is supported..." # Main execution
install_pre_reqs || handle_error $? "Failed installing pre-requisites"
if [ -x /usr/bin/curl ]; then configure_repo "$NODE_VERSION" || handle_error $? "Failed configuring repository"
exec_cmd_nobail "curl -sLf -o /dev/null 'https://deb.nodesource.com/${NODEREPO}/dists/${DISTRO}/Release'"
RC=$?
else
exec_cmd_nobail "wget -qO /dev/null -o /dev/null 'https://deb.nodesource.com/${NODEREPO}/dists/${DISTRO}/Release'"
RC=$?
fi
if [[ $RC != 0 ]]; then
print_status "Your distribution, identified as \"${DISTRO}\", is not currently supported, please contact NodeSource at https://github.com/nodesource/distributions/issues if you think this is incorrect or would like your distribution to be considered for support"
exit 1
fi
if [ -f "/etc/apt/sources.list.d/chris-lea-node_js-$DISTRO.list" ]; then
print_status 'Removing Launchpad PPA Repository for NodeJS...'
exec_cmd_nobail 'add-apt-repository -y -r ppa:chris-lea/node.js'
exec_cmd "rm -f /etc/apt/sources.list.d/chris-lea-node_js-${DISTRO}.list"
fi
print_status 'Adding the NodeSource signing key to your keyring...'
keyring='/usr/share/keyrings'
node_key_url="https://deb.nodesource.com/gpgkey/nodesource.gpg.key"
local_node_key="$keyring/nodesource.gpg"
if [ -x /usr/bin/curl ]; then
exec_cmd "curl -s $node_key_url | gpg --dearmor | tee $local_node_key >/dev/null"
else
exec_cmd "wget -q -O - $node_key_url | gpg --dearmor | tee $local_node_key >/dev/null"
fi
print_status "Creating apt sources list file for the NodeSource ${NODENAME} repo..."
exec_cmd "echo 'deb [signed-by=$local_node_key] https://deb.nodesource.com/${NODEREPO} ${DISTRO} main' > /etc/apt/sources.list.d/nodesource.list"
exec_cmd "echo 'deb-src [signed-by=$local_node_key] https://deb.nodesource.com/${NODEREPO} ${DISTRO} main' >> /etc/apt/sources.list.d/nodesource.list"
print_status 'Running `apt-get update` for you...'
exec_cmd 'apt-get update'
yarn_site='https://dl.yarnpkg.com/debian'
yarn_key_url="$yarn_site/pubkey.gpg"
local_yarn_key="$keyring/yarnkey.gpg"
print_status """Run \`${bold}sudo apt-get install -y ${NODEPKG}${normal}\` to install ${NODENAME} and npm
## You may also need development tools to build native addons:
sudo apt-get install gcc g++ make
## To install the Yarn package manager, run:
curl -sL $yarn_key_url | gpg --dearmor | sudo tee $local_yarn_key >/dev/null
echo \"deb [signed-by=$local_yarn_key] $yarn_site stable main\" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get install yarn
"""
}
## Defer setup until we have the complete script
setup

View File

@@ -2,8 +2,8 @@
"name": "@openstapps/openstapps", "name": "@openstapps/openstapps",
"private": true, "private": true,
"engines": { "engines": {
"node": ">=18.16", "node": "^18.19.1",
"pnpm": ">=8" "pnpm": "^8.15.4"
}, },
"scripts": { "scripts": {
"build": "dotenv -c -- turbo run build", "build": "dotenv -c -- turbo run build",
@@ -13,11 +13,12 @@
"deploy": "dotenv -c -- turbo run deploy --concurrency=1", "deploy": "dotenv -c -- turbo run deploy --concurrency=1",
"dev": "dotenv -c -- turbo run dev", "dev": "dotenv -c -- turbo run dev",
"docs": "dotenv -c -- turbo run docs && typedoc && mkdir docs/api && cp packages/core/lib/api-doc.html docs/api/index.html && cp packages/core/lib/openapi.json docs/api/openapi.json && cp -r packages/core/lib/schema docs/api/schema", "docs": "dotenv -c -- turbo run docs && typedoc && mkdir docs/api && cp packages/core/lib/api-doc.html docs/api/index.html && cp packages/core/lib/openapi.json docs/api/openapi.json && cp -r packages/core/lib/schema docs/api/schema",
"docs:serve": "http-server docs -p 8080 -o",
"format": "dotenv -c -- turbo run format", "format": "dotenv -c -- turbo run format",
"format:fix": "dotenv -c -- turbo run format:fix", "format:fix": "dotenv -c -- turbo run format:fix",
"lint": "dotenv -c -- turbo run lint", "lint": "dotenv -c -- turbo run lint",
"lint:fix": "dotenv -c -- turbo run lint:fix", "lint:fix": "dotenv -c -- turbo run lint:fix",
"publish-packages": "pnpm changeset version && pnpm syncpack:fix && pnpm install && git add . && git commit -m \"docs: update changelogs for release\n\nci: publish release\" && git push && pnpm changeset tag && git push --follow-tags", "publish-packages": "pnpm changeset version && pnpm syncpack:fix && pnpm install && pnpm format:fix && git add . && git commit -m \"docs: update changelogs for release\" -m \"ci: publish release\" && git push && pnpm changeset tag && git push --follow-tags",
"syncpack": "syncpack list-mismatches && syncpack lint-semver-ranges --types dev,peer,prod", "syncpack": "syncpack list-mismatches && syncpack lint-semver-ranges --types dev,peer,prod",
"syncpack:fix": "syncpack format && syncpack fix-mismatches", "syncpack:fix": "syncpack format && syncpack fix-mismatches",
"test": "trap 'node coverage.mjs' EXIT && dotenv -c -- turbo run test", "test": "trap 'node coverage.mjs' EXIT && dotenv -c -- turbo run test",
@@ -34,6 +35,7 @@
"deepmerge": "4.3.1", "deepmerge": "4.3.1",
"dotenv-cli": "7.2.1", "dotenv-cli": "7.2.1",
"glob": "10.3.10", "glob": "10.3.10",
"http-server": "14.1.1",
"junit-report-merger": "6.0.3", "junit-report-merger": "6.0.3",
"prettier": "3.1.1", "prettier": "3.1.1",
"syncpack": "12.3.0", "syncpack": "12.3.0",

View File

@@ -1,12 +1,35 @@
# @openstapps/api-cli # @openstapps/api-cli
## 3.3.2
### Patch Changes
- @openstapps/core@3.3.2
- @openstapps/core-tools@3.3.2
- @openstapps/api@3.3.2
## 3.3.0
### Minor Changes
- 688bc5f2: v3.3.0 changes
### Patch Changes
- Updated dependencies [688bc5f2]
- @openstapps/api@3.3.0
- @openstapps/core@3.3.0
- @openstapps/eslint-config@3.0.0
- @openstapps/core-tools@3.3.0
- @openstapps/logger@3.0.0
## 3.2.0 ## 3.2.0
### Patch Changes ### Patch Changes
- Updated dependencies [912ae422] - Updated dependencies [912ae422]
- @openstapps/core@4.0.0 - @openstapps/core@3.2.0
- @openstapps/api@4.0.0 - @openstapps/api@3.2.0
- @openstapps/core-tools@3.0.0 - @openstapps/core-tools@3.0.0
- @openstapps/logger@3.0.0 - @openstapps/logger@3.0.0

View File

@@ -1,7 +1,7 @@
{ {
"name": "@openstapps/api-cli", "name": "@openstapps/api-cli",
"description": "CLI client for @openstapps/api", "description": "CLI client for @openstapps/api",
"version": "3.2.0", "version": "3.3.2",
"type": "module", "type": "module",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"repository": "git@gitlab.com:openstapps/api.git", "repository": "git@gitlab.com:openstapps/api.git",

View File

@@ -1,12 +1,30 @@
# @openstapps/api-plugin # @openstapps/api-plugin
## 3.3.2
### Patch Changes
- @openstapps/core@3.3.2
- @openstapps/core-tools@3.3.2
- @openstapps/api@3.3.2
## 3.3.0
### Patch Changes
- Updated dependencies [688bc5f2]
- @openstapps/api@3.3.0
- @openstapps/core@3.3.0
- @openstapps/core-tools@3.3.0
- @openstapps/logger@3.0.0
## 3.2.0 ## 3.2.0
### Patch Changes ### Patch Changes
- Updated dependencies [912ae422] - Updated dependencies [912ae422]
- @openstapps/core@4.0.0 - @openstapps/core@3.2.0
- @openstapps/api@4.0.0 - @openstapps/api@3.2.0
- @openstapps/core-tools@3.0.0 - @openstapps/core-tools@3.0.0
- @openstapps/logger@3.0.0 - @openstapps/logger@3.0.0

View File

@@ -1,7 +1,7 @@
{ {
"name": "@openstapps/api-plugin", "name": "@openstapps/api-plugin",
"description": "Node.js library to interact with the StApps backend service", "description": "Node.js library to interact with the StApps backend service",
"version": "3.2.0", "version": "3.3.2",
"type": "module", "type": "module",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"repository": "git@gitlab.com:openstapps/api.git", "repository": "git@gitlab.com:openstapps/api.git",

View File

@@ -1,11 +1,28 @@
# @openstapps/api # @openstapps/api
## 3.3.2
### Patch Changes
- @openstapps/core@3.3.2
## 3.3.0
### Minor Changes
- 688bc5f2: v3.3.0 changes
### Patch Changes
- Updated dependencies [688bc5f2]
- @openstapps/core@3.3.0
## 3.2.0 ## 3.2.0
### Patch Changes ### Patch Changes
- Updated dependencies [912ae422] - Updated dependencies [912ae422]
- @openstapps/core@4.0.0 - @openstapps/core@3.2.0
## 3.1.1 ## 3.1.1

View File

@@ -1,7 +1,7 @@
{ {
"name": "@openstapps/api", "name": "@openstapps/api",
"description": "Node.js library to interact with the StApps backend service", "description": "Node.js library to interact with the StApps backend service",
"version": "3.2.0", "version": "3.3.2",
"type": "module", "type": "module",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"repository": "git@gitlab.com:openstapps/api.git", "repository": "git@gitlab.com:openstapps/api.git",

View File

@@ -1,5 +1,21 @@
# @openstapps/core-tools # @openstapps/core-tools
## 3.3.2
### Patch Changes
- Updated dependencies [802a7a3f]
- @openstapps/easy-ast@3.3.2
## 3.3.0
### Patch Changes
- Updated dependencies [688bc5f2]
- @openstapps/easy-ast@3.3.0
- @openstapps/collection-utils@3.0.0
- @openstapps/logger@3.0.0
## 3.0.0 ## 3.0.0
### Major Changes ### Major Changes

View File

@@ -1,7 +1,7 @@
{ {
"name": "@openstapps/core-tools", "name": "@openstapps/core-tools",
"description": "Tools to convert and validate StAppsCore", "description": "Tools to convert and validate StAppsCore",
"version": "3.0.0", "version": "3.3.2",
"type": "module", "type": "module",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"repository": "git@gitlab.com:openstapps/core-tools.git", "repository": "git@gitlab.com:openstapps/core-tools.git",

View File

@@ -1,5 +1,21 @@
# @openstapps/core # @openstapps/core
## 3.3.2
### Patch Changes
- @openstapps/core-tools@3.3.2
## 3.3.0
### Minor Changes
- 688bc5f2: v3.3.0 changes
### Patch Changes
- @openstapps/core-tools@3.3.0
## 3.2.0 ## 3.2.0
### Minor Changes ### Minor Changes

View File

@@ -1,7 +1,7 @@
{ {
"name": "@openstapps/core", "name": "@openstapps/core",
"description": "StAppsCore - Generalized model of data", "description": "StAppsCore - Generalized model of data",
"version": "3.2.0", "version": "3.3.2",
"type": "module", "type": "module",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"repository": "git@gitlab.com:openstapps/core.git", "repository": "git@gitlab.com:openstapps/core.git",

View File

@@ -1,5 +1,22 @@
# @openstapps/easy-ast # @openstapps/easy-ast
## 3.3.2
### Patch Changes
- 802a7a3f: Fixed docs generation
## 3.3.0
### Minor Changes
- 688bc5f2: v3.3.0 changes
### Patch Changes
- @openstapps/collection-utils@3.0.0
- @openstapps/logger@3.0.0
## 3.0.0 ## 3.0.0
### Major Changes ### Major Changes

View File

@@ -1,7 +1,7 @@
{ {
"name": "@openstapps/easy-ast", "name": "@openstapps/easy-ast",
"description": "Tool to easily handle TypeScript AST", "description": "Tool to easily handle TypeScript AST",
"version": "3.0.0", "version": "3.3.2",
"type": "module", "type": "module",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"repository": "git@gitlab.com:openstapps/core-tools.git", "repository": "git@gitlab.com:openstapps/core-tools.git",

View File

@@ -17,10 +17,8 @@ import {EasyAstSpecType} from '../easy-ast-spec-type.js';
import {LightweightDefinitionKind} from '../../src/index.js'; import {LightweightDefinitionKind} from '../../src/index.js';
import {TypeFlags} from 'typescript'; import {TypeFlags} from 'typescript';
// @ts-expect-error unused type
type TestTypeAlias = number | string; type TestTypeAlias = number | string;
// @ts-expect-error unused type
enum TestEnum { enum TestEnum {
Foo, Foo,
Bar, Bar,

View File

@@ -18,13 +18,9 @@ import {LightweightDefinitionKind} from '../../src/index.js';
interface Random {} interface Random {}
// @ts-expect-error unused type
type TestArrayGeneric = Array<string>; type TestArrayGeneric = Array<string>;
// @ts-expect-error unused type
type TestArrayLiteral = number[]; type TestArrayLiteral = number[];
// @ts-expect-error unused type
type TestArrayReferenceGeneric = Array<Random>; type TestArrayReferenceGeneric = Array<Random>;
// @ts-expect-error unused type
type TestArrayReferenceLiteral = Random[]; type TestArrayReferenceLiteral = Random[];
export const testConfig: EasyAstSpecType = { export const testConfig: EasyAstSpecType = {

View File

@@ -16,12 +16,10 @@
import {EasyAstSpecType} from '../easy-ast-spec-type.js'; import {EasyAstSpecType} from '../easy-ast-spec-type.js';
import {LightweightDefinitionKind} from '../../src/index.js'; import {LightweightDefinitionKind} from '../../src/index.js';
// @ts-expect-error unused type
interface TestInterface { interface TestInterface {
foo: number; foo: number;
} }
// @ts-expect-error unused type
class TestClass { class TestClass {
bar: string = 'test'; bar: string = 'test';
} }

View File

@@ -22,10 +22,8 @@ import {LightweightDefinitionKind} from '../../src/index.js';
* Class description * Class description
* *
* More description * More description
*
* @classTag classParameter1 classParameter2 * @classTag classParameter1 classParameter2
*/ */
// @ts-expect-error unused type
interface TestInterface { interface TestInterface {
/** /**
* Property comment * Property comment
@@ -33,7 +31,6 @@ interface TestInterface {
* Property description * Property description
* *
* More description * More description
*
* @propertyTag propertyParameter1 propertyParameter2 * @propertyTag propertyParameter1 propertyParameter2
*/ */
foo: string; foo: string;
@@ -45,10 +42,8 @@ interface TestInterface {
* Class description * Class description
* *
* More description * More description
*
* @classTag classParameter1 classParameter2 * @classTag classParameter1 classParameter2
*/ */
// @ts-expect-error unused type
class TestClass { class TestClass {
/** /**
* Property comment * Property comment
@@ -56,7 +51,6 @@ class TestClass {
* Property description * Property description
* *
* More description * More description
*
* @propertyTag propertyParameter1 propertyParameter2 * @propertyTag propertyParameter1 propertyParameter2
*/ */
foo = 1; foo = 1;
@@ -68,10 +62,8 @@ class TestClass {
* Enum description * Enum description
* *
* More description * More description
*
* @enumTag enumParameter1 * @enumTag enumParameter1
*/ */
// @ts-expect-error unused type
enum TestAlias {} enum TestAlias {}
export const testConfig: EasyAstSpecType = { export const testConfig: EasyAstSpecType = {

View File

@@ -20,7 +20,6 @@ interface Test1<T = number> {
foo: T; foo: T;
} }
// @ts-expect-error unused type
interface Test2 { interface Test2 {
bar: Test1; bar: Test1;
} }

View File

@@ -17,13 +17,11 @@ import {EasyAstSpecType} from '../easy-ast-spec-type.js';
import {LightweightDefinitionKind} from '../../src/index.js'; import {LightweightDefinitionKind} from '../../src/index.js';
import {TypeFlags} from 'typescript'; import {TypeFlags} from 'typescript';
// @ts-expect-error unused type
enum TestAuto { enum TestAuto {
Foo, Foo,
Bar, Bar,
} }
// @ts-expect-error unused type
enum TestSpecified { enum TestSpecified {
YES = 'yes', YES = 'yes',
NO = 'no', NO = 'no',

Some files were not shown because too many files have changed in this diff Show More