Compare commits

..

11 Commits

Author SHA1 Message Date
1628849d1b feat: update to node 22 2024-06-04 11:13:58 +02:00
91e0f2a255 feat: temporarily use external node image in ci 2024-06-03 13:18:51 +02:00
0a99aa4bde refactor: update node to 22 2024-06-03 12:35:38 +02:00
851a7460aa fix: test 2024-05-30 14:54:29 +02:00
1d56db6bfe fix: build 2024-05-30 14:44:58 +02:00
09376bde02 refactor: update to ionic v8
refactor: remove deprecated @ionic-native/core (replaced by
@awesome-cordova-plugins/core)
2024-05-30 14:18:45 +02:00
0bf41bf569 refactor: update ngx-markdown to v18 2024-05-30 14:09:35 +02:00
7421006c08 refactor: update capacitor to v6 2024-05-30 14:07:33 +02:00
1b02d44f4b fix: angular warning 2024-05-30 13:48:39 +02:00
b8ced7ffa1 refactor: base migration to Angular v18 2024-05-30 13:44:34 +02:00
dbc8ff97aa refactor: remove deprecated "jetifier" 2024-05-30 13:30:01 +02:00
87 changed files with 5007 additions and 4456 deletions

View File

@@ -30,13 +30,11 @@ variables:
description: Bypass turbo cache description: Bypass turbo cache
default: default:
image: registry.gitlab.com/openstapps/openstapps/node-builder image: registry.gitlab.com/openstapps/openstapps/node-builder:node-22
tags: tags:
- saas-linux-xlarge-amd64 - saas-linux-xlarge-amd64
interruptible: true interruptible: true
before_script: before_script:
- corepack enable
- corepack prepare pnpm@latest-8 --activate
- echo TURBO_API=$TURBO_API >> .env.local - echo TURBO_API=$TURBO_API >> .env.local
- echo TURBO_TOKEN=$TURBO_TOKEN >> .env.local - echo TURBO_TOKEN=$TURBO_TOKEN >> .env.local
- echo TURBO_TEAM=$TURBO_TEAM >> .env.local - echo TURBO_TEAM=$TURBO_TEAM >> .env.local
@@ -101,7 +99,7 @@ stop review:
rules: *deploy-rules rules: *deploy-rules
unit: unit:
image: registry.gitlab.com/openstapps/openstapps/app-builder image: registry.gitlab.com/openstapps/openstapps/app-builder:node-22
stage: test stage: test
needs: ['build'] needs: ['build']
script: script:

View File

@@ -27,6 +27,7 @@ const config = {
'bin', 'bin',
'files', 'files',
'engines', 'engines',
'packageManager',
'scripts', 'scripts',
'dependencies', 'dependencies',
'devDependencies', 'devDependencies',

View File

@@ -1,3 +1,3 @@
nodejs 18.19.1 nodejs 22.2.0
pnpm 8.15.4 pnpm 8.15.5
python 3.11.5 python 3.11.5

View File

@@ -1,5 +1,5 @@
integration: integration:
image: registry.gitlab.com/openstapps/openstapps/node-builder image: registry.gitlab.com/openstapps/openstapps/node-builder:node-22
stage: test stage: test
needs: ['build'] needs: ['build']
variables: variables:

View File

@@ -1,18 +1,5 @@
# @openstapps/backend # @openstapps/backend
## 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!
## Completely new map view ## Completelty 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.3.0", "version": "3.2.0",
"private": true, "private": true,
"type": "module", "type": "module",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",

View File

@@ -1,11 +1,5 @@
# @openstapps/tsconfig # @openstapps/tsconfig
## 3.3.0
### Minor Changes
- 688bc5f2: v3.3.0 changes
## 3.0.0 ## 3.0.0
### Major Changes ### Major Changes
@@ -36,7 +30,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
@@ -111,7 +105,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.3.0", "version": "3.0.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

@@ -9,7 +9,7 @@ const args = files.map(it => `${it.split('/', 2)[1]}='${it}'`);
console.log( console.log(
'Collecting coverage...', 'Collecting coverage...',
await promisify(exec)(`cobertura-merge -o ./coverage.xml ${args.join(' ')}`), await promisify(exec)(`merge-cobertura -o ./coverage.xml ${args.join(' ')}`),
); );
const reportFiles = await glob('./*/*/coverage/report-junit.xml'); const reportFiles = await glob('./*/*/coverage/report-junit.xml');

View File

@@ -1,21 +1,12 @@
# @openstapps/minimal-connector # @openstapps/minimal-connector
## 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@3.2.0 - @openstapps/core@4.0.0
- @openstapps/api@3.2.0 - @openstapps/api@4.0.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.3.0", "version": "3.2.0",
"private": true, "private": true,
"type": "module", "type": "module",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",

View File

@@ -1,24 +1,13 @@
# @openstapps/minimal-plugin # @openstapps/minimal-plugin
## 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@3.2.0 - @openstapps/core@4.0.0
- @openstapps/api@3.2.0 - @openstapps/api@4.0.0
- @openstapps/api-plugin@3.2.0 - @openstapps/api-plugin@4.0.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.3.0", "version": "3.2.0",
"private": true, "private": true,
"type": "module", "type": "module",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",

12
flake.lock generated
View File

@@ -5,11 +5,11 @@
"systems": "systems" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1709126324, "lastModified": 1710146030,
"narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=", "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "d465f4819400de7c8d874d50b982301f28a84605", "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -20,11 +20,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1709747860, "lastModified": 1717112898,
"narHash": "sha256-RT4zuBy579m+l8VyIQFOR66WXfcs4g1jntZUHjh6eoI=", "narHash": "sha256-7R2ZvOnvd9h8fDd65p0JnB7wXfUvreox3xFdYWd1BnY=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "58ae79ea707579c40102ddf62d84b902a987c58b", "rev": "6132b0f6e344ce2fe34fc051b72fb46e34f668e0",
"type": "github" "type": "github"
}, },
"original": { "original": {

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 = "34.0.0";
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: {
}; 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 = prev.lib.lists.unique [
"34.0.0"
aapt2buildToolsVersion
];
platformVersions = [ "34" ];
}; };
}); cypress = prev.cypress.overrideAttrs (cyPrev: rec {
nodejs = prev.nodejs_18; version = "13.10.0";
}) src = prev.fetchzip {
]; url = "https://cdn.cypress.io/desktop/${version}/linux-x64/cypress.zip";
config = { hash = "sha256-wKNXo2lWndsQaouOiul0rsOWah+RRQ6fljzdeC8/KW4=";
allowUnfree = true; };
android_sdk.accept_license = true; });
nodejs = prev.nodejs_22;
})
];
config = {
allowUnfree = true;
android_sdk.accept_license = true;
};
}; };
}; androidFhs = pkgs.buildFHSUserEnv {
androidFhs = pkgs.buildFHSUserEnv { name = "android-env";
name = "android-env"; runScript = "bash";
targetPkgs = pkgs: with pkgs; []; profile = ''
runScript = "bash"; export ALLOW_NINJA_ENV=true
profile = '' export USE_CCACHE=1
export ALLOW_NINJA_ENV=true export LD_LIBRARY_PATH=/usr/lib:/usr/lib32
export USE_CCACHE=1 '';
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 nodePackages.pnpm
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"; # https://github.com/nodejs/node/issues/48444#issuecomment-1591882694
}; UV_USE_IO_URING = "0";
}); };
}
);
} }

View File

@@ -1,5 +1,5 @@
e2e: e2e:
image: registry.gitlab.com/openstapps/openstapps/app-cypress:node-18 image: registry.gitlab.com/openstapps/openstapps/app-cypress:node-22
stage: test stage: test
script: script:
- pnpm --filter=@openstapps/app install - pnpm --filter=@openstapps/app install

View File

@@ -1,18 +1,5 @@
# @openstapps/app # @openstapps/app
## 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

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,98 +86,52 @@ addToIonicDB(
You'll need to run _Chromium_ using You'll need to run _Chromium_ using
```sh ```shell
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. server. You can circumvent this locally by using the `:virtual-host`
scripts:
#### Web ```shell
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
``` ```
@@ -185,7 +139,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
@@ -205,7 +159,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
``` ```
@@ -215,13 +169,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,7 +9,6 @@ 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')
@@ -22,7 +21,6 @@ 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,8 +1,4 @@
[ [
{
"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"
@@ -51,10 +47,6 @@
"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,57 +1,51 @@
// 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.7.3_@capacitor+core@5.7.3/node_modules/@capacitor/android/capacitor') project(':capacitor-android').projectDir = new File('../../../node_modules/.pnpm/@capacitor+android@6.0.0_@capacitor+core@6.0.0/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@5.7.3/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.7_@capacitor+core@5.7.3/node_modules/@capacitor/app/android') project(':capacitor-app').projectDir = new File('../../../node_modules/.pnpm/@capacitor+app@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/app/android')
include ':capacitor-browser' include ':capacitor-browser'
project(':capacitor-browser').projectDir = new File('../../../node_modules/.pnpm/@capacitor+browser@5.2.0_@capacitor+core@5.7.3/node_modules/@capacitor/browser/android') project(':capacitor-browser').projectDir = new File('../../../node_modules/.pnpm/@capacitor+browser@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/browser/android')
include ':capacitor-clipboard' include ':capacitor-clipboard'
project(':capacitor-clipboard').projectDir = new File('../../../node_modules/.pnpm/@capacitor+clipboard@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/clipboard/android') project(':capacitor-clipboard').projectDir = new File('../../../node_modules/.pnpm/@capacitor+clipboard@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/clipboard/android')
include ':capacitor-device' include ':capacitor-device'
project(':capacitor-device').projectDir = new File('../../../node_modules/.pnpm/@capacitor+device@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/device/android') project(':capacitor-device').projectDir = new File('../../../node_modules/.pnpm/@capacitor+device@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/device/android')
include ':capacitor-dialog' include ':capacitor-dialog'
project(':capacitor-dialog').projectDir = new File('../../../node_modules/.pnpm/@capacitor+dialog@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/dialog/android') project(':capacitor-dialog').projectDir = new File('../../../node_modules/.pnpm/@capacitor+dialog@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/dialog/android')
include ':capacitor-filesystem' include ':capacitor-filesystem'
project(':capacitor-filesystem').projectDir = new File('../../../node_modules/.pnpm/@capacitor+filesystem@5.2.1_@capacitor+core@5.7.3/node_modules/@capacitor/filesystem/android') project(':capacitor-filesystem').projectDir = new File('../../../node_modules/.pnpm/@capacitor+filesystem@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/filesystem/android')
include ':capacitor-geolocation' include ':capacitor-geolocation'
project(':capacitor-geolocation').projectDir = new File('../../../node_modules/.pnpm/@capacitor+geolocation@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/geolocation/android') project(':capacitor-geolocation').projectDir = new File('../../../node_modules/.pnpm/@capacitor+geolocation@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/geolocation/android')
include ':capacitor-haptics' include ':capacitor-haptics'
project(':capacitor-haptics').projectDir = new File('../../../node_modules/.pnpm/@capacitor+haptics@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/haptics/android') project(':capacitor-haptics').projectDir = new File('../../../node_modules/.pnpm/@capacitor+haptics@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/haptics/android')
include ':capacitor-keyboard' include ':capacitor-keyboard'
project(':capacitor-keyboard').projectDir = new File('../../../node_modules/.pnpm/@capacitor+keyboard@5.0.8_@capacitor+core@5.7.3/node_modules/@capacitor/keyboard/android') project(':capacitor-keyboard').projectDir = new File('../../../node_modules/.pnpm/@capacitor+keyboard@6.0.0_@capacitor+core@6.0.0/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.7_@capacitor+core@5.7.3/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.0.0/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.7_@capacitor+core@5.7.3/node_modules/@capacitor/network/android') project(':capacitor-network').projectDir = new File('../../../node_modules/.pnpm/@capacitor+network@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/network/android')
include ':capacitor-preferences' include ':capacitor-preferences'
project(':capacitor-preferences').projectDir = new File('../../../node_modules/.pnpm/@capacitor+preferences@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/preferences/android') project(':capacitor-preferences').projectDir = new File('../../../node_modules/.pnpm/@capacitor+preferences@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/preferences/android')
include ':capacitor-screen-orientation'
project(':capacitor-screen-orientation').projectDir = new File('../../../node_modules/.pnpm/@capacitor+screen-orientation@6.0.0_@capacitor+core@5.7.3/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.7_@capacitor+core@5.7.3/node_modules/@capacitor/share/android') project(':capacitor-share').projectDir = new File('../../../node_modules/.pnpm/@capacitor+share@6.0.0_@capacitor+core@6.0.0/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.7_@capacitor+core@5.7.3/node_modules/@capacitor/splash-screen/android') project(':capacitor-splash-screen').projectDir = new File('../../../node_modules/.pnpm/@capacitor+splash-screen@6.0.0_@capacitor+core@6.0.0/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.2.0_@capacitor+core@5.7.3/node_modules/@transistorsoft/capacitor-background-fetch/android') project(':transistorsoft-capacitor-background-fetch').projectDir = new File('../../../node_modules/.pnpm/@transistorsoft+capacitor-background-fetch@6.0.0_@capacitor+core@6.0.0/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.7.3/node_modules/capacitor-secure-storage-plugin/android') project(':capacitor-secure-storage-plugin').projectDir = new File('../../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.10.0_@capacitor+core@6.0.0/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

@@ -1,11 +1,11 @@
#!/usr/bin/env bash #!/usr/bin/env bash
rm coverage/integration-report-junit-*.xml rm coverage/integration-report-junit-*.xml || true
ng e2e --watch=false --headless=true --browser="$BROWSER" ng e2e --watch=false --headless=true --browser="$BROWSER"
exit_code=$? exit_code=$?
jrm coverage/integration-report-junit.xml coverage/integration-report-junit-*.xml jrm coverage/integration-report-junit.xml coverage/integration-report-junit-*.xml || true
rm coverage/integration-report-junit-*.xml rm coverage/integration-report-junit-*.xml || true
exit $exit_code exit $exit_code

View File

@@ -1,4 +1,4 @@
require_relative '../../../../node_modules/.pnpm/@capacitor+ios@5.7.3_@capacitor+core@5.7.3/node_modules/@capacitor/ios/scripts/pods_helpers' require_relative '../../../../node_modules/.pnpm/@capacitor+ios@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/ios/scripts/pods_helpers'
platform :ios, '13.0' platform :ios, '13.0'
use_frameworks! use_frameworks!
@@ -9,26 +9,24 @@ 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.7.3_@capacitor+core@5.7.3/node_modules/@capacitor/ios' pod 'Capacitor', :path => '../../../../node_modules/.pnpm/@capacitor+ios@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/ios'
pod 'CapacitorCordova', :path => '../../../../node_modules/.pnpm/@capacitor+ios@5.7.3_@capacitor+core@5.7.3/node_modules/@capacitor/ios' pod 'CapacitorCordova', :path => '../../../../node_modules/.pnpm/@capacitor+ios@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/ios'
pod 'CapacitorCommunityScreenBrightness', :path => '../../../../node_modules/.pnpm/@capacitor-community+screen-brightness@6.0.0_@capacitor+core@5.7.3/node_modules/@capacitor-community/screen-brightness' pod 'CapacitorApp', :path => '../../../../node_modules/.pnpm/@capacitor+app@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/app'
pod 'CapacitorApp', :path => '../../../../node_modules/.pnpm/@capacitor+app@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/app' pod 'CapacitorBrowser', :path => '../../../../node_modules/.pnpm/@capacitor+browser@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/browser'
pod 'CapacitorBrowser', :path => '../../../../node_modules/.pnpm/@capacitor+browser@5.2.0_@capacitor+core@5.7.3/node_modules/@capacitor/browser' pod 'CapacitorClipboard', :path => '../../../../node_modules/.pnpm/@capacitor+clipboard@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/clipboard'
pod 'CapacitorClipboard', :path => '../../../../node_modules/.pnpm/@capacitor+clipboard@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/clipboard' pod 'CapacitorDevice', :path => '../../../../node_modules/.pnpm/@capacitor+device@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/device'
pod 'CapacitorDevice', :path => '../../../../node_modules/.pnpm/@capacitor+device@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/device' pod 'CapacitorDialog', :path => '../../../../node_modules/.pnpm/@capacitor+dialog@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/dialog'
pod 'CapacitorDialog', :path => '../../../../node_modules/.pnpm/@capacitor+dialog@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/dialog' pod 'CapacitorFilesystem', :path => '../../../../node_modules/.pnpm/@capacitor+filesystem@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/filesystem'
pod 'CapacitorFilesystem', :path => '../../../../node_modules/.pnpm/@capacitor+filesystem@5.2.1_@capacitor+core@5.7.3/node_modules/@capacitor/filesystem' pod 'CapacitorGeolocation', :path => '../../../../node_modules/.pnpm/@capacitor+geolocation@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/geolocation'
pod 'CapacitorGeolocation', :path => '../../../../node_modules/.pnpm/@capacitor+geolocation@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/geolocation' pod 'CapacitorHaptics', :path => '../../../../node_modules/.pnpm/@capacitor+haptics@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/haptics'
pod 'CapacitorHaptics', :path => '../../../../node_modules/.pnpm/@capacitor+haptics@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/haptics' pod 'CapacitorKeyboard', :path => '../../../../node_modules/.pnpm/@capacitor+keyboard@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/keyboard'
pod 'CapacitorKeyboard', :path => '../../../../node_modules/.pnpm/@capacitor+keyboard@5.0.8_@capacitor+core@5.7.3/node_modules/@capacitor/keyboard' pod 'CapacitorLocalNotifications', :path => '../../../../node_modules/.pnpm/@capacitor+local-notifications@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/local-notifications'
pod 'CapacitorLocalNotifications', :path => '../../../../node_modules/.pnpm/@capacitor+local-notifications@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/local-notifications' pod 'CapacitorNetwork', :path => '../../../../node_modules/.pnpm/@capacitor+network@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/network'
pod 'CapacitorNetwork', :path => '../../../../node_modules/.pnpm/@capacitor+network@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/network' pod 'CapacitorPreferences', :path => '../../../../node_modules/.pnpm/@capacitor+preferences@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/preferences'
pod 'CapacitorPreferences', :path => '../../../../node_modules/.pnpm/@capacitor+preferences@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/preferences' pod 'CapacitorShare', :path => '../../../../node_modules/.pnpm/@capacitor+share@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/share'
pod 'CapacitorScreenOrientation', :path => '../../../../node_modules/.pnpm/@capacitor+screen-orientation@6.0.0_@capacitor+core@5.7.3/node_modules/@capacitor/screen-orientation' pod 'CapacitorSplashScreen', :path => '../../../../node_modules/.pnpm/@capacitor+splash-screen@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/splash-screen'
pod 'CapacitorShare', :path => '../../../../node_modules/.pnpm/@capacitor+share@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/share' pod 'TransistorsoftCapacitorBackgroundFetch', :path => '../../../../node_modules/.pnpm/@transistorsoft+capacitor-background-fetch@5.2.0_@capacitor+core@6.0.0/node_modules/@transistorsoft/capacitor-background-fetch'
pod 'CapacitorSplashScreen', :path => '../../../../node_modules/.pnpm/@capacitor+splash-screen@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/splash-screen' pod 'CapacitorSecureStoragePlugin', :path => '../../../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@6.0.0/node_modules/capacitor-secure-storage-plugin'
pod 'TransistorsoftCapacitorBackgroundFetch', :path => '../../../../node_modules/.pnpm/@transistorsoft+capacitor-background-fetch@5.2.0_@capacitor+core@5.7.3/node_modules/@transistorsoft/capacitor-background-fetch'
pod 'CapacitorSecureStoragePlugin', :path => '../../../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@5.7.3/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,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.3.0", "version": "3.2.0",
"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>",
@@ -14,9 +14,7 @@
"Thea Schöbl <dev@theaninova.de>" "Thea Schöbl <dev@theaninova.de>"
], ],
"scripts": { "scripts": {
"analyze": "webpack-bundle-analyzer www/stats.json", "build": "pnpm check-icons && ng build --configuration=production --stats-json",
"build": "pnpm check-icons && ng build --configuration=production --stats-json && webpack-bundle-analyzer www/stats.json --mode static --report www/bundle-info.html --no-open",
"build:analyze": "npm run build:stats && npm run analyze",
"build:android": "ionic capacitor build android --no-open && cd android && ./gradlew clean assemble && cd ..", "build:android": "ionic capacitor build android --no-open && cd android && ./gradlew clean assemble && cd ..",
"build:prod": "ng build --configuration=production", "build:prod": "ng build --configuration=production",
"build:stats": "ng build --configuration=production --stats-json", "build:stats": "ng build --configuration=production --stats-json",
@@ -39,7 +37,6 @@
"lint": "ng lint && stylelint \"**/*.scss\"", "lint": "ng lint && stylelint \"**/*.scss\"",
"lint:fix": "eslint --fix -c .eslintrc.json --ignore-path .eslintignore --ext .ts,.html src/ && stylelint --fix \"**/*.scss\"", "lint:fix": "eslint --fix -c .eslintrc.json --ignore-path .eslintignore --ext .ts,.html src/ && stylelint --fix \"**/*.scss\"",
"minify-icons": "node scripts/minify-icon-font.mjs", "minify-icons": "node scripts/minify-icon-font.mjs",
"postinstall": "jetify && echo \"skipping jetify in production mode\"",
"preview": "http-server www --p 8101 -o", "preview": "http-server www --p 8101 -o",
"push": "git push && git push origin \"v$npm_package_version\"", "push": "git push && git push origin \"v$npm_package_version\"",
"resources:ios": "capacitor-assets generate --ios --iconBackgroundColor $(grep -oE \"^@include ion-color\\(primary, #[a-fA-F0-9]{3,6}\" src/theme/colors.scss | grep -oE \"#[a-fA-F0-9]{3,6}\") --splashBackgroundColor $(grep -oE \"^@include ion-color\\(primary, #[a-fA-F0-9]{3,6}\" src/theme/colors.scss | grep -oE \"#[a-fA-F0-9]{3,6}\")", "resources:ios": "capacitor-assets generate --ios --iconBackgroundColor $(grep -oE \"^@include ion-color\\(primary, #[a-fA-F0-9]{3,6}\" src/theme/colors.scss | grep -oE \"#[a-fA-F0-9]{3,6}\") --splashBackgroundColor $(grep -oE \"^@include ion-color\\(primary, #[a-fA-F0-9]{3,6}\" src/theme/colors.scss | grep -oE \"#[a-fA-F0-9]{3,6}\")",
@@ -52,34 +49,31 @@
"test:integration": "sh integration-test.sh" "test:integration": "sh integration-test.sh"
}, },
"dependencies": { "dependencies": {
"@angular/animations": "17.3.0", "@angular/animations": "18.0.1",
"@angular/cdk": "17.3.0", "@angular/cdk": "18.0.1",
"@angular/common": "17.3.0", "@angular/common": "18.0.1",
"@angular/core": "17.3.0", "@angular/core": "18.0.1",
"@angular/forms": "17.3.0", "@angular/forms": "18.0.1",
"@angular/platform-browser": "17.3.0", "@angular/platform-browser": "18.0.1",
"@angular/router": "17.3.0", "@angular/router": "18.0.1",
"@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-community/screen-brightness": "6.0.0", "@capacitor/app": "6.0.0",
"@capacitor/app": "5.0.7", "@capacitor/browser": "6.0.0",
"@capacitor/browser": "5.2.0", "@capacitor/clipboard": "6.0.0",
"@capacitor/clipboard": "5.0.7", "@capacitor/core": "6.0.0",
"@capacitor/core": "5.7.3", "@capacitor/device": "6.0.0",
"@capacitor/device": "5.0.7", "@capacitor/dialog": "6.0.0",
"@capacitor/dialog": "5.0.7", "@capacitor/filesystem": "6.0.0",
"@capacitor/filesystem": "5.2.1", "@capacitor/geolocation": "6.0.0",
"@capacitor/geolocation": "5.0.7", "@capacitor/haptics": "6.0.0",
"@capacitor/haptics": "5.0.7", "@capacitor/keyboard": "6.0.0",
"@capacitor/keyboard": "5.0.8", "@capacitor/local-notifications": "6.0.0",
"@capacitor/local-notifications": "5.0.7", "@capacitor/network": "6.0.0",
"@capacitor/network": "5.0.7", "@capacitor/preferences": "6.0.0",
"@capacitor/preferences": "5.0.7", "@capacitor/share": "6.0.0",
"@capacitor/screen-orientation": "6.0.0", "@capacitor/splash-screen": "6.0.0",
"@capacitor/share": "5.0.7", "@ionic/angular": "8.2.0",
"@capacitor/splash-screen": "5.0.7",
"@ionic-native/core": "5.36.0",
"@ionic/angular": "7.8.0",
"@ionic/storage-angular": "4.0.0", "@ionic/storage-angular": "4.0.0",
"@maplibre/ngx-maplibre-gl": "17.4.1", "@maplibre/ngx-maplibre-gl": "17.4.1",
"@ngx-translate/core": "15.0.0", "@ngx-translate/core": "15.0.0",
@@ -88,9 +82,9 @@
"@openstapps/api": "workspace:*", "@openstapps/api": "workspace:*",
"@openstapps/collection-utils": "workspace:*", "@openstapps/collection-utils": "workspace:*",
"@openstapps/core": "workspace:*", "@openstapps/core": "workspace:*",
"@transistorsoft/capacitor-background-fetch": "5.2.0", "@transistorsoft/capacitor-background-fetch": "6.0.0",
"@types/dom-view-transitions": "1.0.4", "@types/dom-view-transitions": "1.0.4",
"capacitor-secure-storage-plugin": "0.9.0", "capacitor-secure-storage-plugin": "0.10.0",
"cordova-plugin-calendar": "5.1.6", "cordova-plugin-calendar": "5.1.6",
"date-fns": "3.6.0", "date-fns": "3.6.0",
"deepmerge": "4.3.1", "deepmerge": "4.3.1",
@@ -103,10 +97,11 @@
"moment": "2.30.1", "moment": "2.30.1",
"ngx-date-fns": "11.0.0", "ngx-date-fns": "11.0.0",
"ngx-logger": "5.0.12", "ngx-logger": "5.0.12",
"ngx-markdown": "17.1.1", "ngx-markdown": "18.0.0",
"ngx-moment": "6.0.2", "ngx-moment": "6.0.2",
"opening_hours": "3.8.0", "opening_hours": "3.8.0",
"pmtiles": "3.0.3", "pmtiles": "3.0.3",
"prettier": "3.1.1",
"rxjs": "7.8.1", "rxjs": "7.8.1",
"semver": "7.6.0", "semver": "7.6.0",
"swiper": "8.4.5", "swiper": "8.4.5",
@@ -114,25 +109,25 @@
"zone.js": "0.14.4" "zone.js": "0.14.4"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/architect": "0.1703.0", "@angular-devkit/architect": "0.1800.2",
"@angular-devkit/build-angular": "17.3.0", "@angular-devkit/build-angular": "18.0.2",
"@angular-devkit/core": "17.3.0", "@angular-devkit/core": "18.0.2",
"@angular-devkit/schematics": "17.3.0", "@angular-devkit/schematics": "18.0.2",
"@angular-eslint/builder": "17.3.0", "@angular-eslint/builder": "18.0.1",
"@angular-eslint/eslint-plugin": "17.3.0", "@angular-eslint/eslint-plugin": "18.0.1",
"@angular-eslint/eslint-plugin-template": "17.3.0", "@angular-eslint/eslint-plugin-template": "18.0.1",
"@angular-eslint/schematics": "17.3.0", "@angular-eslint/schematics": "18.0.1",
"@angular-eslint/template-parser": "17.3.0", "@angular-eslint/template-parser": "18.0.1",
"@angular/cli": "17.3.0", "@angular/cli": "18.0.2",
"@angular/compiler": "17.3.0", "@angular/compiler": "18.0.1",
"@angular/compiler-cli": "17.3.0", "@angular/compiler-cli": "18.0.1",
"@angular/language-server": "17.3.0", "@angular/language-server": "17.3.0",
"@angular/language-service": "17.3.0", "@angular/language-service": "18.0.1",
"@angular/platform-browser-dynamic": "17.3.0", "@angular/platform-browser-dynamic": "18.0.1",
"@capacitor/android": "5.7.3", "@capacitor/android": "6.0.0",
"@capacitor/assets": "3.0.4", "@capacitor/assets": "3.0.4",
"@capacitor/cli": "5.7.3", "@capacitor/cli": "6.0.0",
"@capacitor/ios": "5.7.3", "@capacitor/ios": "6.0.0",
"@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",
@@ -153,7 +148,7 @@
"@typescript-eslint/eslint-plugin": "7.2.0", "@typescript-eslint/eslint-plugin": "7.2.0",
"@typescript-eslint/parser": "7.2.0", "@typescript-eslint/parser": "7.2.0",
"cordova-res": "0.15.4", "cordova-res": "0.15.4",
"cypress": "13.7.0", "cypress": "13.10.0",
"eslint": "8.57.0", "eslint": "8.57.0",
"eslint-plugin-jsdoc": "48.2.1", "eslint-plugin-jsdoc": "48.2.1",
"eslint-plugin-prettier": "5.1.3", "eslint-plugin-prettier": "5.1.3",
@@ -165,7 +160,6 @@
"is-docker": "2.2.1", "is-docker": "2.2.1",
"jasmine-core": "5.1.2", "jasmine-core": "5.1.2",
"jasmine-spec-reporter": "7.0.0", "jasmine-spec-reporter": "7.0.0",
"jetifier": "2.0.0",
"junit-report-merger": "6.0.3", "junit-report-merger": "6.0.3",
"karma": "6.4.3", "karma": "6.4.3",
"karma-chrome-launcher": "3.2.0", "karma-chrome-launcher": "3.2.0",
@@ -181,8 +175,7 @@
"stylelint-config-standard-scss": "13.0.0", "stylelint-config-standard-scss": "13.0.0",
"surge": "0.23.1", "surge": "0.23.1",
"ts-node": "10.9.2", "ts-node": "10.9.2",
"typescript": "5.4.2", "typescript": "5.4.2"
"webpack-bundle-analyzer": "4.10.1"
}, },
"cordova": { "cordova": {
"plugins": {}, "plugins": {},

View File

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

View File

@@ -21,7 +21,7 @@ import {ModalController, Platform} from '@ionic/angular';
import {TranslateService} from '@ngx-translate/core'; import {TranslateService} from '@ngx-translate/core';
import {ThingTranslateService} from './translation/thing-translate.service'; import {ThingTranslateService} from './translation/thing-translate.service';
import {HttpClientTestingModule} from '@angular/common/http/testing'; import {provideHttpClientTesting} from '@angular/common/http/testing';
import {AppComponent} from './app.component'; import {AppComponent} from './app.component';
import {AuthModule} from './modules/auth/auth.module'; import {AuthModule} from './modules/auth/auth.module';
import {ConfigProvider} from './modules/config/config.provider'; import {ConfigProvider} from './modules/config/config.provider';
@@ -32,6 +32,7 @@ import {ScheduleSyncService} from './modules/background/schedule/schedule-sync.s
import {sampleAuthConfiguration} from './_helpers/data/sample-configuration'; import {sampleAuthConfiguration} from './_helpers/data/sample-configuration';
import {StorageProvider} from './modules/storage/storage.provider'; import {StorageProvider} from './modules/storage/storage.provider';
import {SimpleBrowser} from './util/browser.factory'; import {SimpleBrowser} from './util/browser.factory';
import {provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
describe('AppComponent', () => { describe('AppComponent', () => {
let platformReadySpy: any; let platformReadySpy: any;
@@ -75,8 +76,9 @@ describe('AppComponent', () => {
modalController = jasmine.createSpyObj('ModalController', ['create', 'dismiss', 'getTop']); modalController = jasmine.createSpyObj('ModalController', ['create', 'dismiss', 'getTop']);
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes([]), HttpClientTestingModule, AuthModule],
declarations: [AppComponent], declarations: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [RouterTestingModule.withRoutes([]), AuthModule],
providers: [ providers: [
{provide: Platform, useValue: platformSpy}, {provide: Platform, useValue: platformSpy},
{provide: TranslateService, useValue: translateServiceSpy}, {provide: TranslateService, useValue: translateServiceSpy},
@@ -88,8 +90,9 @@ describe('AppComponent', () => {
{provide: StorageProvider, useValue: storageProvider}, {provide: StorageProvider, useValue: storageProvider},
{provide: SimpleBrowser, useValue: simpleBrowser}, {provide: SimpleBrowser, useValue: simpleBrowser},
{provide: ModalController, useValue: modalController}, {provide: ModalController, useValue: modalController},
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
], ],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents(); }).compileComponents();
}); });

View File

@@ -13,7 +13,7 @@
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {CommonModule, LocationStrategy, PathLocationStrategy, registerLocaleData} from '@angular/common'; import {CommonModule, LocationStrategy, PathLocationStrategy, registerLocaleData} from '@angular/common';
import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule} from '@angular/common/http'; import {HTTP_INTERCEPTORS, HttpClient, provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
import localeDe from '@angular/common/locales/de'; import localeDe from '@angular/common/locales/de';
import {APP_INITIALIZER, NgModule} from '@angular/core'; import {APP_INITIALIZER, NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser'; import {BrowserModule} from '@angular/platform-browser';
@@ -161,7 +161,6 @@ export function createTranslateLoader(http: HttpClient) {
JobModule, JobModule,
FavoritesModule, FavoritesModule,
LibraryModule, LibraryModule,
HttpClientModule,
ProfilePageModule, ProfilePageModule,
FeedbackModule, FeedbackModule,
MapModule, MapModule,
@@ -221,6 +220,7 @@ export function createTranslateLoader(http: HttpClient) {
useClass: ServiceHandlerInterceptor, useClass: ServiceHandlerInterceptor,
multi: true, multi: true,
}, },
provideHttpClient(withInterceptorsFromDi()),
], ],
}) })
export class AppModule { export class AppModule {

View File

@@ -12,6 +12,24 @@
* 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

@@ -22,7 +22,7 @@ import {Requestor, StorageBackend} from '@openid/appauth';
import {TranslateService} from '@ngx-translate/core'; import {TranslateService} from '@ngx-translate/core';
import {PAIAAuthService} from './paia/paia-auth.service'; import {PAIAAuthService} from './paia/paia-auth.service';
import {StAppsWebHttpClient} from '../data/stapps-web-http-client.provider'; import {StAppsWebHttpClient} from '../data/stapps-web-http-client.provider';
import {HttpClientModule} from '@angular/common/http'; import {provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
import {SimpleBrowser} from '../../util/browser.factory'; import {SimpleBrowser} from '../../util/browser.factory';
import {LoggerTestingModule} from 'ngx-logger/testing'; import {LoggerTestingModule} from 'ngx-logger/testing';
@@ -54,7 +54,7 @@ describe('AuthHelperService', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [HttpClientModule, LoggerTestingModule], imports: [LoggerTestingModule],
providers: [ providers: [
StAppsWebHttpClient, StAppsWebHttpClient,
{ {
@@ -88,6 +88,7 @@ describe('AuthHelperService', () => {
provide: SimpleBrowser, provide: SimpleBrowser,
useValue: simpleBrowserMock, useValue: simpleBrowserMock,
}, },
provideHttpClient(withInterceptorsFromDi()),
], ],
}); });
authHelperService = TestBed.inject(AuthHelperService); authHelperService = TestBed.inject(AuthHelperService);

View File

@@ -20,7 +20,7 @@ import {Browser} from 'ionic-appauth';
import {nowInSeconds, Requestor, StorageBackend} from '@openid/appauth'; import {nowInSeconds, Requestor, StorageBackend} from '@openid/appauth';
import {TranslateService} from '@ngx-translate/core'; import {TranslateService} from '@ngx-translate/core';
import {StAppsWebHttpClient} from '../data/stapps-web-http-client.provider'; import {StAppsWebHttpClient} from '../data/stapps-web-http-client.provider';
import {HttpClientModule} from '@angular/common/http'; import {provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
import {IonicStorage} from 'ionic-appauth/lib'; import {IonicStorage} from 'ionic-appauth/lib';
import {RouterModule} from '@angular/router'; import {RouterModule} from '@angular/router';
import {LoggerTestingModule} from 'ngx-logger/testing'; import {LoggerTestingModule} from 'ngx-logger/testing';
@@ -35,7 +35,7 @@ describe('AuthService', () => {
storageBackendSpy = jasmine.createSpyObj('StorageBackend', ['getItem']); storageBackendSpy = jasmine.createSpyObj('StorageBackend', ['getItem']);
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [HttpClientModule, LoggerTestingModule, RouterModule.forRoot([])], imports: [LoggerTestingModule, RouterModule.forRoot([])],
providers: [ providers: [
StAppsWebHttpClient, StAppsWebHttpClient,
{ {
@@ -54,6 +54,7 @@ describe('AuthService', () => {
useValue: storageBackendSpy, useValue: storageBackendSpy,
}, },
Requestor, Requestor,
provideHttpClient(withInterceptorsFromDi()),
], ],
}); });
defaultAuthService = TestBed.inject(DefaultAuthService); defaultAuthService = TestBed.inject(DefaultAuthService);

View File

@@ -24,17 +24,13 @@ 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']);
@@ -55,10 +51,6 @@ describe('ConfigProvider', () => {
provide: NGXLogger, provide: NGXLogger,
useValue: ngxLogger, useValue: ngxLogger,
}, },
{
provide: InternetConnectionService,
useValue: internetConnectionServiceMock,
},
], ],
}); });
@@ -83,22 +75,6 @@ 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));

View File

@@ -27,8 +27,6 @@ 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
@@ -74,7 +72,6 @@ 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);
@@ -85,15 +82,9 @@ export class ConfigProvider {
*/ */
async fetch(): Promise<SCIndexResponse> { async fetch(): Promise<SCIndexResponse> {
try { try {
const isOffline = await firstValueFrom(this.internetConnectionService.offline$); return await this.client.handshake(this.scVersion);
if (isOffline) { } catch {
throw new Error('Device is offline.'); throw new ConfigFetchError();
} else {
return await this.client.handshake(this.scVersion);
}
} catch (error) {
const error_ = error instanceof Error ? new ConfigFetchError(error.message) : new ConfigFetchError();
throw error_;
} }
} }

View File

@@ -19,10 +19,8 @@ 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(reason?: string) { constructor() {
const defaultMessage = 'App configuration could not be fetched!'; super('ConfigFetchError', 'App configuration could not be fetched!');
const message = reason ? `${defaultMessage} ${reason}` : defaultMessage;
super('ConfigFetchError', message);
} }
} }

View File

@@ -37,8 +37,5 @@
{{ 'dashboard.jobs.noJobs' | translate }} {{ 'dashboard.jobs.noJobs' | translate }}
</ion-label> </ion-label>
</ion-item> </ion-item>
<ion-button slot="button-end" fill="clear" color="medium" [routerLink]="['/jobs']">
<ion-icon slot="icon-only" name="search" [size]="24"></ion-icon>
</ion-button>
} }
</stapps-section> </stapps-section>

View File

@@ -14,7 +14,7 @@
*/ */
import {ScrollingModule} from '@angular/cdk/scrolling'; import {ScrollingModule} from '@angular/cdk/scrolling';
import {CommonModule} from '@angular/common'; import {CommonModule} from '@angular/common';
import {HttpClientModule} from '@angular/common/http'; import {provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms'; import {FormsModule} from '@angular/forms';
import {IonicModule, Platform} from '@ionic/angular'; import {IonicModule, Platform} from '@ionic/angular';
@@ -181,12 +181,33 @@ import {ShareButtonComponent} from './elements/share-button.component';
PeriodicalDetailContentComponent, PeriodicalDetailContentComponent,
ShareButtonComponent, ShareButtonComponent,
], ],
exports: [
DataDetailComponent,
DataDetailContentComponent,
DataIconPipe,
DataListComponent,
DataListItemComponent,
DateSeriesListItemComponent,
PlaceListItemComponent,
SimpleCardComponent,
SkeletonListItemComponent,
SkeletonSimpleCardComponent,
SearchPageComponent,
SimpleDataListComponent,
OriginDetailComponent,
FavoriteButtonComponent,
TreeListComponent,
ExternalLinkComponent,
ArticleDetailContentComponent,
BookDetailContentComponent,
PeriodicalDetailContentComponent,
TitleCardComponent,
],
imports: [ imports: [
CommonModule, CommonModule,
DataRoutingModule, DataRoutingModule,
FormsModule, FormsModule,
MapWidgetComponent, MapWidgetComponent,
HttpClientModule,
IonicModule.forRoot(), IonicModule.forRoot(),
MarkdownModule.forRoot(), MarkdownModule.forRoot(),
MenuModule, MenuModule,
@@ -218,28 +239,7 @@ import {ShareButtonComponent} from './elements/share-button.component';
useFactory: browserFactory, useFactory: browserFactory,
deps: [Platform], deps: [Platform],
}, },
], provideHttpClient(withInterceptorsFromDi()),
exports: [
DataDetailComponent,
DataDetailContentComponent,
DataIconPipe,
DataListComponent,
DataListItemComponent,
DateSeriesListItemComponent,
PlaceListItemComponent,
SimpleCardComponent,
SkeletonListItemComponent,
SkeletonSimpleCardComponent,
SearchPageComponent,
SimpleDataListComponent,
OriginDetailComponent,
FavoriteButtonComponent,
TreeListComponent,
ExternalLinkComponent,
ArticleDetailContentComponent,
BookDetailContentComponent,
PeriodicalDetailContentComponent,
TitleCardComponent,
], ],
}) })
export class DataModule {} export class DataModule {}

View File

@@ -21,7 +21,7 @@ import {StAppsWebHttpClient} from '../data/stapps-web-http-client.provider';
import {StorageProvider} from '../storage/storage.provider'; import {StorageProvider} from '../storage/storage.provider';
import {LoggerTestingModule} from 'ngx-logger/testing'; import {LoggerTestingModule} from 'ngx-logger/testing';
import {MapModule} from '../map/map.module'; import {MapModule} from '../map/map.module';
import {HttpClientModule} from '@angular/common/http'; import {provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
import {StorageModule} from '../storage/storage.module'; import {StorageModule} from '../storage/storage.module';
import {DaiaHolding, DaiaService} from './protocol/response'; import {DaiaHolding, DaiaService} from './protocol/response';
import {Observable, of} from 'rxjs'; import {Observable, of} from 'rxjs';
@@ -47,7 +47,6 @@ describe('DaiaDataProvider', () => {
imports: [ imports: [
HebisModule, HebisModule,
MapModule, MapModule,
HttpClientModule,
StorageModule, StorageModule,
LoggerTestingModule, LoggerTestingModule,
TranslateModule.forRoot({ TranslateModule.forRoot({
@@ -62,6 +61,7 @@ describe('DaiaDataProvider', () => {
StAppsWebHttpClient, StAppsWebHttpClient,
StorageProvider, StorageProvider,
DaiaDataProvider, DaiaDataProvider,
provideHttpClient(withInterceptorsFromDi()),
], ],
}); });
daiaDataProvider = TestBed.inject(DaiaDataProvider); daiaDataProvider = TestBed.inject(DaiaDataProvider);

View File

@@ -14,7 +14,7 @@
*/ */
import {ScrollingModule} from '@angular/cdk/scrolling'; import {ScrollingModule} from '@angular/cdk/scrolling';
import {CommonModule} from '@angular/common'; import {CommonModule} from '@angular/common';
import {HttpClientModule} from '@angular/common/http'; import {provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms'; import {FormsModule} from '@angular/forms';
import {IonicModule} from '@ionic/angular'; import {IonicModule} from '@ionic/angular';
@@ -54,7 +54,6 @@ import {DaiaHoldingComponent} from './daia-availability/daia-holding.component';
FormsModule, FormsModule,
HebisRoutingModule, HebisRoutingModule,
IonIconModule, IonIconModule,
HttpClientModule,
IonicModule.forRoot(), IonicModule.forRoot(),
MarkdownModule.forRoot(), MarkdownModule.forRoot(),
MenuModule, MenuModule,
@@ -69,6 +68,11 @@ import {DaiaHoldingComponent} from './daia-availability/daia-holding.component';
ThingTranslateModule.forChild(), ThingTranslateModule.forChild(),
UtilModule, UtilModule,
], ],
providers: [HebisDataProvider, DaiaDataProvider, StAppsWebHttpClient], providers: [
HebisDataProvider,
DaiaDataProvider,
StAppsWebHttpClient,
provideHttpClient(withInterceptorsFromDi()),
],
}) })
export class HebisModule {} export class HebisModule {}

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.getPatron(); const patron = await this.libraryAccountService.getProfile();
this.name = patron?.name; this.name = patron?.name;
} }
} }

View File

@@ -39,8 +39,6 @@ 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', 'duedate']" [propertiesToShow]="['label', 'renewals', 'endtime']"
(documentAction)="onDocumentAction($event)" (documentAction)="onDocumentAction($event)"
listName="checked_out" listName="checked_out"
> >

View File

@@ -14,8 +14,7 @@
*/ */
import {Component, EventEmitter, Input, Output} from '@angular/core'; import {Component, EventEmitter, Input, Output} from '@angular/core';
import {DocumentAction, PAIADocument, PAIADocumentStatus} from '../../../types'; import {DocumentAction, PAIADocument} from '../../../types';
import {LibraryAccountService} from '../../library-account.service';
@Component({ @Component({
selector: 'stapps-paia-item', selector: 'stapps-paia-item',
@@ -23,21 +22,7 @@ import {LibraryAccountService} from '../../library-account.service';
styleUrls: ['./paiaitem.scss'], styleUrls: ['./paiaitem.scss'],
}) })
export class PAIAItemComponent { export class PAIAItemComponent {
private _item: PAIADocument; @Input() item: PAIADocument;
renewable: boolean;
constructor(private readonly libraryAccountService: LibraryAccountService) {}
@Input()
set item(value: PAIADocument) {
this._item = value;
void this.setRenewable();
}
get item(): PAIADocument {
return this._item;
}
@Input() @Input()
propertiesToShow: (keyof PAIADocument)[]; propertiesToShow: (keyof PAIADocument)[];
@@ -51,9 +36,4 @@ 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;
}
} }

View File

@@ -23,7 +23,7 @@
@if (item[property]) { @if (item[property]) {
<p> <p>
{{ 'library.account.pages' + '.' + listName + '.' + 'labels' + '.' + property | translate }}: {{ 'library.account.pages' + '.' + listName + '.' + 'labels' + '.' + property | translate }}:
@if (!['starttime', 'duedate'].includes(property)) { @if (!['endtime', 'duedate'].includes(property)) {
{{ item[property] }} {{ item[property] }}
} @else { } @else {
{{ $any(item[property]) | amDateFormat: 'll' }} {{ $any(item[property]) | amDateFormat: 'll' }}
@@ -40,7 +40,7 @@
<!-- >--> <!-- >-->
<!-- {{ 'library.account.actions.cancel.header' | translate }}</ion-button--> <!-- {{ 'library.account.actions.cancel.header' | translate }}</ion-button-->
<!-- >--> <!-- >-->
@if (renewable && item.canrenew) { @if (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

@@ -45,8 +45,6 @@ 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

@@ -58,7 +58,7 @@
@for (hold of paiaDocuments; track hold) { @for (hold of paiaDocuments; track hold) {
<stapps-paia-item <stapps-paia-item
[item]="hold" [item]="hold"
[propertiesToShow]="['label', 'starttime', 'storage', 'queue']" [propertiesToShow]="['label']"
(documentAction)="onDocumentAction($event)" (documentAction)="onDocumentAction($event)"
listName="holds" listName="holds"
> >

View File

@@ -20,15 +20,7 @@ import {
SCFeatureConfiguration, SCFeatureConfiguration,
SCFeatureConfigurationExtern, SCFeatureConfigurationExtern,
} from '@openstapps/core'; } from '@openstapps/core';
import { import {DocumentAction, PAIADocument, PAIADocumentStatus, PAIAFees, PAIAItems, PAIAPatron} from '../types';
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';
@@ -51,11 +43,6 @@ 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,
@@ -73,23 +60,12 @@ export class LibraryAccountService {
this.authType = config.authProvider as SCAuthorizationProviderType; this.authType = config.authProvider as SCAuthorizationProviderType;
} }
async getPatron() { async getProfile() {
const patronId = ((await this.getValidToken()) as PAIATokenResponse).patron; const patron = ((await this.getValidToken()) as PAIATokenResponse).patron;
const patron = { return {
...(await this.performRequest<PAIAPatron>(`${this.baseUrl}/{patron}`)), ...(await this.performRequest<PAIAPatron>(`${this.baseUrl}/{patron}`)),
id: patronId, id: patron,
} 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,19 +25,15 @@ import {PAIAPatron} from '../../types';
export class ProfilePageComponent { export class ProfilePageComponent {
patron?: PAIAPatron; patron?: PAIAPatron;
propertiesToShow: (keyof PAIAPatron)[] = ['id', 'name', 'email', 'address', 'expires']; 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.getPatron(); this.patron = await this.libraryAccountService.getProfile();
} catch { } catch {
await this.libraryAccountService.handleError(); await this.libraryAccountService.handleError();
} }
} }
isUnlimitedExpiry(date = ''): boolean {
return new Date(date).getFullYear() === 9999;
}
} }

View File

@@ -36,11 +36,11 @@
@if (!['expires'].includes(property)) { @if (!['expires'].includes(property)) {
{{ patron[property] }} {{ patron[property] }}
} @else { } @else {
@if (isUnlimitedExpiry(patron['expires'])) { @if (patron[property] === '9999-12-31') {
{{ 'library.account.pages.profile.values.unlimited' | translate }} {{ 'library.account.pages.profile.values.unlimited' | translate }}
} @else { } @else {
{{ 'library.account.pages.profile.values.expires' | translate }}:&nbsp;{{ {{ 'library.account.pages.profile.values.expires' | translate }}:&nbsp;{{
patron['expires'] | amDateFormat: 'll' patron[property] | amDateFormat: 'll'
}} }}
} }
} }

View File

@@ -19,19 +19,11 @@ export interface PAIAPatron {
email?: string; email?: string;
address?: string; address?: string;
expires?: string; expires?: string;
status?: PAIAPatronStatus; status?: string;
type?: string; type?: string;
note?: string; note?: string;
} }
export enum PAIAPatronStatus {
Active = 0,
Inactive = 1,
InactiveExpired = 2,
InactiveOutstandingFees = 3,
InactiveExpiredOutstandingFees = 4,
}
/* /*
* Document representing a library item received from the HeBIS PAIA service * Document representing a library item received from the HeBIS PAIA service
* TODO: would be good to standardize the items of HeBIS PAIA to match the official PAIA documentation * TODO: would be good to standardize the items of HeBIS PAIA to match the official PAIA documentation
@@ -47,7 +39,7 @@ export interface PAIADocument {
queue?: string; queue?: string;
renewals?: string; renewals?: string;
reminder?: string; reminder?: string;
starttime?: string; endtime?: string;
duedate?: string; duedate?: string;
cancancel?: boolean; cancancel?: boolean;
canrenew?: boolean; canrenew?: boolean;

View File

@@ -1,6 +1,5 @@
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]',
@@ -11,7 +10,7 @@ export class MapStyleDirective {
@Input() @Input()
set styleName(name: string) { set styleName(name: string) {
const style = `${environment.backend_url}/_static/map/styles/${name}/style.json`; const style = `https://maps.server.uni-frankfurt.de/static/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

@@ -14,7 +14,7 @@
*/ */
import {TestBed} from '@angular/core/testing'; import {TestBed} from '@angular/core/testing';
import {MapModule} from './map.module'; import {MapModule} from './map.module';
import {HttpClientModule} from '@angular/common/http'; import {provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
import {StorageModule} from '../storage/storage.module'; import {StorageModule} from '../storage/storage.module';
import {MapPosition, PositionService} from './position.service'; import {MapPosition, PositionService} from './position.service';
import {ConfigProvider} from '../config/config.provider'; import {ConfigProvider} from '../config/config.provider';
@@ -39,12 +39,13 @@ describe('PositionService', () => {
}, },
}); });
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [MapModule, HttpClientModule, StorageModule, LoggerTestingModule], imports: [MapModule, StorageModule, LoggerTestingModule],
providers: [ providers: [
{ {
provider: ConfigProvider, provider: ConfigProvider,
useValue: configProviderMock, useValue: configProviderMock,
}, },
provideHttpClient(withInterceptorsFromDi()),
], ],
}); });
positionService = TestBed.inject(PositionService); positionService = TestBed.inject(PositionService);

View File

@@ -1,14 +1,10 @@
import {ChangeDetectionStrategy, Component, ElementRef, Input} from '@angular/core'; import {ChangeDetectionStrategy, Component, 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 {InRangeNowPipe, ToDateRangePipe} 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',
@@ -16,130 +12,17 @@ import {iosDuration, iosEasing, mdDuration, mdEasing} from 'src/app/animation/ea
styleUrls: ['id-card.scss'], styleUrls: ['id-card.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true, standalone: true,
imports: [ThingTranslateModule, InRangeNowPipe, ToDateRangePipe, AsyncPipe, TranslateModule, TitleCasePipe], hostDirectives: [FullScreenImageDirective],
imports: [
FullScreenImageDirective,
ThingTranslateModule,
InRangeNowPipe,
ToDateRangePipe,
AsyncPipe,
TranslateModule,
TitleCasePipe,
],
}) })
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,9 +1,4 @@
<img <img [src]="item.image" [alt]="'name' | thingTranslate: item" draggable="false" />
[src]="item.image"
[alt]="'name' | thingTranslate: item"
draggable="false"
(click)="presentFullscreen()"
/>
@if (item.validity && (item.validity | toDateRange | isInRangeNow | async) === false) { @if (item.validity && (item.validity | toDateRange | isInRangeNow | async) === false) {
<div class="expired">{{ 'profile.userInfo.expired' | translate | titlecase }}</div> <div class="expired">{{ 'profile.userInfo.expired' | translate | titlecase }}</div>
} }

View File

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

View File

@@ -19,6 +19,7 @@ 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';
@@ -35,6 +36,7 @@ import {Observable} from 'rxjs';
AsyncPipe, AsyncPipe,
ThingTranslateModule, ThingTranslateModule,
UtilModule, UtilModule,
FullScreenImageDirective,
IdCardComponent, IdCardComponent,
TranslateModule, TranslateModule,
TitleCasePipe, TitleCasePipe,

View File

@@ -1,11 +1,10 @@
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, of, Observable} from 'rxjs'; import {from, Observable, of} from 'rxjs';
import {AuthHelperService} from '../auth/auth-helper.service'; import {AuthHelperService} from '../auth/auth-helper.service';
import {mergeMap, concatWith, filter, map, startWith, catchError, tap} from 'rxjs/operators'; import {mergeMap, filter, map, startWith} 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 {
@@ -13,27 +12,18 @@ 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.isAuthenticated$.pipe(
mergeMap(isAuthenticated => mergeMap(isAuthenticated =>
isAuthenticated isAuthenticated
? feature ? feature
? storedIdCards.pipe( ? from(auth.getValidToken()).pipe(
concatWith( mergeMap(token => this.fetchIdCards(feature.url, token.accessToken)),
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),
@@ -41,20 +31,19 @@ export class IdCardsProvider {
mergeMap(user => this.fetchFallbackIdCards(user)), mergeMap(user => this.fetchFallbackIdCards(user)),
startWith([]), startWith([]),
) )
: of([]).pipe(tap(() => this.encryptedStorageProvider.delete('id-cards'))), : // TODO: find a better solution here (async pipe stuff...)
of([]),
), ),
); );
} }
private fetchIdCards(url: string, token: string): Observable<SCIdCard[]> { private fetchIdCards(url: string, token: string): Observable<SCIdCard[]> {
return this.httpClient return this.httpClient.get<SCIdCard[]>(url, {
.get<SCIdCard[]>(url, { headers: {
headers: { Authorization: `Bearer ${token}`,
Authorization: `Bearer ${token}`, },
}, responseType: 'json',
responseType: 'json', });
})
.pipe(tap(idCards => this.encryptedStorageProvider.set('id-cards', idCards)));
} }
private fetchFallbackIdCards(user: SCUserConfiguration): Observable<SCIdCard[]> { private fetchFallbackIdCards(user: SCUserConfiguration): Observable<SCIdCard[]> {

View File

@@ -4,7 +4,6 @@ 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); isAuthenticated$ = new BehaviorSubject(false);
@@ -17,7 +16,6 @@ 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(() => {
@@ -29,14 +27,10 @@ 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, encryptedStorageProvider); const provider = new IdCardsProvider(authHelper, configProvider, httpClient);
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);
}); });
@@ -45,7 +39,7 @@ describe('IdCards', () => {
fakeAuth.isAuthenticated$.next(true); fakeAuth.isAuthenticated$.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, encryptedStorageProvider); const provider = new IdCardsProvider(authHelper, configProvider, httpClient);
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
@@ -58,7 +52,7 @@ describe('IdCards', () => {
}); });
it('should react to logins', async () => { it('should react to logins', async () => {
const provider = new IdCardsProvider(authHelper, configProvider, httpClient, encryptedStorageProvider); const provider = new IdCardsProvider(authHelper, configProvider, httpClient);
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']));

View File

@@ -15,7 +15,7 @@
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing'; import {ComponentFixture, TestBed} from '@angular/core/testing';
import {HttpClientTestingModule} from '@angular/common/http/testing'; import {provideHttpClientTesting} from '@angular/common/http/testing';
import {RouterTestingModule} from '@angular/router/testing'; import {RouterTestingModule} from '@angular/router/testing';
import {AuthModule} from '../../auth/auth.module'; import {AuthModule} from '../../auth/auth.module';
import {ProfilePageComponent} from './profile-page.component'; import {ProfilePageComponent} from './profile-page.component';
@@ -27,6 +27,7 @@ import {ScheduleProvider} from '../../calendar/schedule.provider';
import {DataProvider} from '../../data/data.provider'; import {DataProvider} from '../../data/data.provider';
import {StAppsWebHttpClient} from '../../data/stapps-web-http-client.provider'; import {StAppsWebHttpClient} from '../../data/stapps-web-http-client.provider';
import {SimpleBrowser} from '../../../util/browser.factory'; import {SimpleBrowser} from '../../../util/browser.factory';
import {provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
describe('ProfilePage', () => { describe('ProfilePage', () => {
let component: ProfilePageComponent; let component: ProfilePageComponent;
@@ -47,7 +48,8 @@ describe('ProfilePage', () => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ProfilePageComponent], declarations: [ProfilePageComponent],
imports: [HttpClientTestingModule, RouterTestingModule, AuthModule, TranslateModule.forRoot()], schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [RouterTestingModule, AuthModule, TranslateModule.forRoot()],
providers: [ providers: [
{provide: ConfigProvider, useValue: configProvider}, {provide: ConfigProvider, useValue: configProvider},
{provide: StorageProvider, useValue: storageProvider}, {provide: StorageProvider, useValue: storageProvider},
@@ -55,8 +57,9 @@ describe('ProfilePage', () => {
{provide: SimpleBrowser, useValue: simpleBrowser}, {provide: SimpleBrowser, useValue: simpleBrowser},
ScheduleProvider, ScheduleProvider,
DataProvider, DataProvider,
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
], ],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents(); }).compileComponents();
}); });

View File

@@ -1,92 +0,0 @@
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

@@ -336,10 +336,8 @@
"title": "Titel", "title": "Titel",
"about": "Mehr Informationen", "about": "Mehr Informationen",
"label": "Signatur", "label": "Signatur",
"starttime": "Übermittelt am",
"endtime": "Abzuholen bis", "endtime": "Abzuholen bis",
"storage": "Abholtheke", "storage": "Abholbereit"
"queue": "Position in der Warteschlange"
}, },
"holds": "Bestellungen", "holds": "Bestellungen",
"reservations": "Vormerkungen" "reservations": "Vormerkungen"
@@ -350,7 +348,7 @@
"title": "Titel", "title": "Titel",
"about": "Mehr Informationen", "about": "Mehr Informationen",
"label": "Signatur", "label": "Signatur",
"duedate": "Leihfristende", "endtime": "Leihfristende",
"renewals": "Verlängerungen" "renewals": "Verlängerungen"
} }
}, },

View File

@@ -336,10 +336,8 @@
"title": "Title", "title": "Title",
"about": "More information", "about": "More information",
"label": "Shelfmark", "label": "Shelfmark",
"starttime": "Submitted at",
"endtime": "Available for pickup until", "endtime": "Available for pickup until",
"storage": "Pick-up counter", "storage": "Available for pickup"
"queue": "Position in the queue"
}, },
"holds": "orders", "holds": "orders",
"reservations": "reservations" "reservations": "reservations"
@@ -350,7 +348,7 @@
"title": "Title", "title": "Title",
"about": "More information", "about": "More information",
"label": "Label", "label": "Label",
"duedate": "Due date", "endtime": "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.3.0', backend_version: '3.1.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.3.0', backend_version: '3.1.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,8 +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" \
NODE_VERSION="22.x" \
NPM_VERSION="^9.0.0" \ NPM_VERSION="^9.0.0" \
IONIC_VERSION="^6.0.0" \ IONIC_VERSION="^6.0.0" \
CORDOVA_RES_VERSION="latest" \ CORDOVA_RES_VERSION="latest" \
@@ -19,7 +20,6 @@ ENV ANDROID_APIS="android-33" \
ANDROID_SDK_ROOT="/opt/android-sdk" \ ANDROID_SDK_ROOT="/opt/android-sdk" \
### Installation files ### Installation files
SCRIPTS_DIRECTORY="scripts" \ SCRIPTS_DIRECTORY="scripts" \
NODE_SETUP_SCRIPT="node_setup.sh" \
TMP_PROJECT_NAME="tmp-project" TMP_PROJECT_NAME="tmp-project"
### Set $PATH ### Set $PATH
@@ -96,25 +96,25 @@ RUN curl $ANDROID_SDK_TOOLS_DOWNLOAD_URL > /tmp/android-sdk.zip && \
sdkmanager "platforms;$ANDROID_APIS" "build-tools;$ANDROID_BUILD_TOOLS_VERSION" sdkmanager "platforms;$ANDROID_APIS" "build-tools;$ANDROID_BUILD_TOOLS_VERSION"
### Copy scripts directory into the tmp folder, so it's available to the following commands ### Copy scripts directory into the tmp folder, so it's available to the following commands
COPY $SCRIPTS_DIRECTORY/$NODE_SETUP_SCRIPT /tmp/
RUN bash /tmp/$NODE_SETUP_SCRIPT && apt-get install -y nodejs && \ RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION} | bash && \
apt-get install -y nodejs && \
### Install wanted npm version ### Install wanted npm version
npm install -g --unsafe-perm npm@$NPM_VERSION && \ npm install -g --unsafe-perm npm@$NPM_VERSION && \
### Install needed global npm packages ### Install needed global npm packages
npm install -g --unsafe-perm @ionic/cli@$IONIC_VERSION cordova-res@$CORDOVA_RES_VERSION npm install -g --unsafe-perm @ionic/cli@$IONIC_VERSION cordova-res@$CORDOVA_RES_VERSION
RUN cd / && ionic start $TMP_PROJECT_NAME blank --type=angular --capacitor --no-git --no-interactive && \ RUN cd / && ionic start $TMP_PROJECT_NAME blank --type=angular --capacitor --no-git --no-interactive && \
cd $TMP_PROJECT_NAME && ionic capacitor add android && export NG_CLI_ANALYTICS=ci && ionic capacitor build android --no-open && \ cd $TMP_PROJECT_NAME && ionic capacitor add android && export NG_CLI_ANALYTICS=ci && ionic capacitor build android --no-open && \
cd android && ./gradlew assembleDebug && \ cd android && ./gradlew assembleDebug && \
cd / && rm -rf $TMP_PROJECT_NAME cd / && rm -rf $TMP_PROJECT_NAME
### Compatibility with musl libc ### Compatibility with musl libc
RUN ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 RUN ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1
### PNPM ### PNPM
RUN corepack enable && corepack prepare pnpm@latest-8 --activate RUN corepack enable
### Set working directory ### Set working directory
WORKDIR /app WORKDIR /app

View File

@@ -1,372 +0,0 @@
#!/bin/bash
# Discussion, issues and change requests at:
# https://github.com/nodesource/distributions
#
# Script to install the NodeSource Node.js 18.x repo onto a
# Debian or Ubuntu system.
#
# Run as root or insert `sudo -E` before `bash`:
#
# 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.
#
export DEBIAN_FRONTEND=noninteractive
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
ncolors=$(which tput > /dev/null && tput colors) # supports color
if test -n "$ncolors" && test $ncolors -ge 8; then
termcols=$(tput cols)
bold="$(tput bold)"
underline="$(tput smul)"
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() {
echo 'Error executing command, exiting'
exit 1
}
exec_cmd_nobail() {
echo "+ $1"
bash -c "$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
}
script_deprecation_warning() {
if [ "X${SCRSUFFIX}" == "X" ]; then
print_bold \
" 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
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 Linux distributions GitHub repository contains
information about which versions of Node.js and which Linux distributions
are supported and how to use the install scripts.
${bold}https://github.com/nodesource/distributions${normal}
"
echo
echo "Continuing in 20 seconds (press Ctrl-C to abort) ..."
echo
sleep 20
fi
}
setup() {
script_deprecation_warning
node_deprecation_warning
print_status "Installing the NodeSource ${NODENAME} repo..."
if $(uname -m | grep -Eq ^armv6); then
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."
exit 1
fi
PRE_INSTALL_PKGS=""
# Check that HTTPS transport is available to APT
# (Check snaked from: https://get.docker.io/ubuntu/)
if [ ! -e /usr/lib/apt/methods/https ]; then
PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} apt-transport-https"
fi
if [ ! -x /usr/bin/lsb_release ]; then
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
}
check_alt "Astra Linux" "orel" "Debian" "stretch"
check_alt "BOSS" "anokha" "Debian" "wheezy"
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
print_status "Unknown Debian-based distribution, checking /etc/debian_version..."
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..."
if [ -x /usr/bin/curl ]; then
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

@@ -1,5 +1,5 @@
### Set base image ### Set base image
FROM cypress/base:18.16.1 FROM cypress/base:22.0.0
USER root USER root
@@ -83,6 +83,9 @@ RUN apt-get update && \
# Add a link to the browser that allows Cypress to find it # Add a link to the browser that allows Cypress to find it
RUN ln -s /usr/bin/microsoft-edge /usr/bin/edge RUN ln -s /usr/bin/microsoft-edge /usr/bin/edge
### PNPM
RUN corepack enable
# versions of local tools # versions of local tools
RUN echo " node version: $(node -v) \n" \ RUN echo " node version: $(node -v) \n" \
"npm version: $(npm -v) \n" \ "npm version: $(npm -v) \n" \

View File

@@ -1,7 +1,9 @@
FROM node:18-alpine3.18 FROM node:22-alpine
RUN apk update && apk add git curl jq && mkdir -p /opt RUN apk update && apk add git curl jq && mkdir -p /opt
RUN corepack enable
COPY --chown=root:root wait-for.sh /opt/wait-for COPY --chown=root:root wait-for.sh /opt/wait-for
RUN chmod +x /opt/wait-for && ln -s /opt/wait-for /usr/local/bin/wait-for RUN chmod +x /opt/wait-for && ln -s /opt/wait-for /usr/local/bin/wait-for

View File

@@ -1,7 +1,7 @@
FROM node:18-alpine3.18 FROM node:22-alpine
RUN apk update && apk add git jq curl python3 build-base RUN apk update && apk add git jq curl python3 build-base
RUN npm install turbo pnpm --global RUN corepack enable
CMD [] CMD []

View File

@@ -2,9 +2,10 @@
"name": "@openstapps/openstapps", "name": "@openstapps/openstapps",
"private": true, "private": true,
"engines": { "engines": {
"node": ">=18.16", "node": "^22.0.0",
"pnpm": ">=8" "pnpm": "^8.15.5"
}, },
"packageManager": "pnpm@8.15.5",
"scripts": { "scripts": {
"build": "dotenv -c -- turbo run build", "build": "dotenv -c -- turbo run build",
"build:full": "dotenv -c -- turbo run syncpack build lint format --summarize", "build:full": "dotenv -c -- turbo run syncpack build lint format --summarize",
@@ -30,15 +31,15 @@
"devDependencies": { "devDependencies": {
"@changesets/changelog-git": "0.1.14", "@changesets/changelog-git": "0.1.14",
"@changesets/cli": "2.26.1", "@changesets/cli": "2.26.1",
"cobertura-merge": "1.0.4", "merge-cobertura": "1.0.1",
"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",
"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",
"turbo": "1.10.16", "turbo": "1.13.3",
"turbo-ignore": "1.10.16", "turbo-ignore": "1.13.3",
"typedoc": "0.25.12", "typedoc": "0.25.12",
"typescript": "5.4.2" "typescript": "5.4.2"
} }

View File

@@ -1,27 +1,12 @@
# @openstapps/api-cli # @openstapps/api-cli
## 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@3.2.0 - @openstapps/core@4.0.0
- @openstapps/api@3.2.0 - @openstapps/api@4.0.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.3.0", "version": "3.2.0",
"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",
@@ -43,7 +43,7 @@
"@types/cli-progress": "3.11.0", "@types/cli-progress": "3.11.0",
"@types/express": "4.17.17", "@types/express": "4.17.17",
"@types/fs-extra": "9.0.13", "@types/fs-extra": "9.0.13",
"@types/json-schema": "7.0.11", "@types/json-schema": "7.0.15",
"@types/junit-report-builder": "3.0.0", "@types/junit-report-builder": "3.0.0",
"@types/mocha": "10.0.1", "@types/mocha": "10.0.1",
"@types/node": "18.15.3", "@types/node": "18.15.3",

View File

@@ -1,22 +1,12 @@
# @openstapps/api-plugin # @openstapps/api-plugin
## 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@3.2.0 - @openstapps/core@4.0.0
- @openstapps/api@3.2.0 - @openstapps/api@4.0.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.3.0", "version": "3.2.0",
"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",
@@ -38,7 +38,7 @@
"@openstapps/logger": "workspace:*", "@openstapps/logger": "workspace:*",
"@types/body-parser": "1.19.2", "@types/body-parser": "1.19.2",
"@types/express": "4.17.17", "@types/express": "4.17.17",
"@types/json-schema": "7.0.11", "@types/json-schema": "7.0.15",
"@types/morgan": "1.9.4", "@types/morgan": "1.9.4",
"body-parser": "1.20.2", "body-parser": "1.20.2",
"express": "4.18.2", "express": "4.18.2",

View File

@@ -1,22 +1,11 @@
# @openstapps/api # @openstapps/api
## 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@3.2.0 - @openstapps/core@4.0.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.3.0", "version": "3.2.0",
"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,14 +1,5 @@
# @openstapps/core-tools # @openstapps/core-tools
## 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.3.0", "version": "3.0.0",
"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",
@@ -62,7 +62,7 @@
"mustache": "4.2.0", "mustache": "4.2.0",
"openapi-types": "12.1.0", "openapi-types": "12.1.0",
"plantuml-encoder": "1.4.0", "plantuml-encoder": "1.4.0",
"re2": "1.18.2", "re2": "1.21.0",
"toposort": "2.0.2", "toposort": "2.0.2",
"ts-json-schema-generator": "1.2.0" "ts-json-schema-generator": "1.2.0"
}, },
@@ -73,7 +73,7 @@
"@types/chai": "4.3.5", "@types/chai": "4.3.5",
"@types/fs-extra": "9.0.13", "@types/fs-extra": "9.0.13",
"@types/glob": "8.1.0", "@types/glob": "8.1.0",
"@types/json-schema": "7.0.11", "@types/json-schema": "7.0.15",
"@types/mocha": "10.0.1", "@types/mocha": "10.0.1",
"@types/mustache": "4.2.2", "@types/mustache": "4.2.2",
"@types/node": "18.15.3", "@types/node": "18.15.3",

View File

@@ -1,15 +1,5 @@
# @openstapps/core # @openstapps/core
## 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.3.0", "version": "3.2.0",
"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",
@@ -60,7 +60,7 @@
"@openstapps/tsconfig": "workspace:*", "@openstapps/tsconfig": "workspace:*",
"@types/chai": "4.3.5", "@types/chai": "4.3.5",
"@types/json-patch": "0.0.30", "@types/json-patch": "0.0.30",
"@types/json-schema": "7.0.11", "@types/json-schema": "7.0.15",
"@types/mocha": "10.0.1", "@types/mocha": "10.0.1",
"@types/node": "18.15.3", "@types/node": "18.15.3",
"c8": "7.14.0", "c8": "7.14.0",

View File

@@ -1,16 +1,5 @@
# @openstapps/easy-ast # @openstapps/easy-ast
## 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.3.0", "version": "3.0.0",
"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",

7799
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff