mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-04-14 18:29:23 +00:00
Compare commits
1 Commits
183-use-ne
...
202-update
| Author | SHA1 | Date | |
|---|---|---|---|
|
69868663cb
|
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@openstapps/easy-ast": patch
|
||||
---
|
||||
|
||||
Fixed docs generation
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@openstapps/backend": patch
|
||||
---
|
||||
|
||||
fix for geo.point mapping
|
||||
@@ -1,3 +1,3 @@
|
||||
nodejs 18.19.1
|
||||
nodejs 22.2.0
|
||||
pnpm 8.15.4
|
||||
python 3.11.5
|
||||
python 3.11.5
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
# @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
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@openstapps/backend",
|
||||
"description": "A reference implementation for a StApps backend",
|
||||
"version": "3.3.0",
|
||||
"version": "3.2.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "AGPL-3.0-only",
|
||||
|
||||
@@ -25,7 +25,7 @@ export function buildDistanceFilter(
|
||||
): QueryDslSpecificQueryContainer<'geo_distance'> {
|
||||
const geoObject: QueryDslGeoDistanceQuery = {
|
||||
distance: `${filter.arguments.distance}m`,
|
||||
[`${filter.arguments.field}.point`]: {
|
||||
[`${filter.arguments.field}.point.coordinates`]: {
|
||||
lat: filter.arguments.position[1],
|
||||
lon: filter.arguments.position[0],
|
||||
},
|
||||
|
||||
@@ -25,7 +25,7 @@ export function buildDistanceSort(sort: SCDistanceSort): SortOptions {
|
||||
mode: 'avg',
|
||||
order: sort.order,
|
||||
unit: 'm',
|
||||
[`${sort.arguments.field}.point`]: {
|
||||
[`${sort.arguments.field}.point.coordinates`]: {
|
||||
lat: sort.arguments.position[1],
|
||||
lon: sort.arguments.position[0],
|
||||
},
|
||||
|
||||
@@ -466,7 +466,7 @@ describe('Query', function () {
|
||||
const expectedFilter: QueryDslSpecificQueryContainer<'geo_distance'> = {
|
||||
geo_distance: {
|
||||
'distance': '1000m',
|
||||
'geo.point': {
|
||||
'geo.point.coordinates': {
|
||||
lat: 8.123,
|
||||
lon: 50.123,
|
||||
},
|
||||
@@ -636,7 +636,7 @@ describe('Query', function () {
|
||||
'mode': 'avg',
|
||||
'order': 'desc',
|
||||
'unit': 'm',
|
||||
'geo.point': {
|
||||
'geo.point.coordinates': {
|
||||
lat: 50.123,
|
||||
lon: 8.123,
|
||||
},
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
# @openstapps/tsconfig
|
||||
|
||||
## 3.3.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 688bc5f2: v3.3.0 changes
|
||||
|
||||
## 3.0.0
|
||||
|
||||
### Major Changes
|
||||
@@ -36,7 +30,7 @@
|
||||
|
||||
```js
|
||||
#!/usr/bin/env node
|
||||
import "./lib/app.js";
|
||||
import './lib/app.js';
|
||||
```
|
||||
|
||||
- 64caebaf: Migrate to ESM
|
||||
@@ -111,7 +105,7 @@
|
||||
|
||||
```js
|
||||
#!/usr/bin/env node
|
||||
import "./lib/app.js";
|
||||
import './lib/app.js';
|
||||
```
|
||||
|
||||
- 64caebaf: Migrate to ESM
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@openstapps/tsconfig",
|
||||
"description": "The tsconfig for the openstapps project",
|
||||
"version": "3.3.0",
|
||||
"version": "3.0.0",
|
||||
"type": "commonjs",
|
||||
"license": "GPL-3.0-only",
|
||||
"repository": "git@gitlab.com:openstapps/eslint-config.git",
|
||||
|
||||
@@ -1,31 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"allowUnreachableCode": false,
|
||||
"checkJs": true,
|
||||
"alwaysStrict": true,
|
||||
"declaration": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true,
|
||||
"explainFiles": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"skipLibCheck": true,
|
||||
"downlevelIteration": true,
|
||||
"inlineSourceMap": true,
|
||||
"isolatedModules": true,
|
||||
"lib": [
|
||||
"ES2022",
|
||||
"DOM"
|
||||
],
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"noErrorTruncation": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitOverride": true,
|
||||
"noImplicitReturns": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"isolatedModules": true,
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"outDir": "../../../lib/",
|
||||
"lib": [
|
||||
"ES2022",
|
||||
"DOM"
|
||||
@@ -37,6 +33,7 @@
|
||||
"transpileOnly": true
|
||||
},
|
||||
"exclude": [
|
||||
"../../../lib/"
|
||||
"../../../app.js",
|
||||
"../../../lib/"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,21 +1,12 @@
|
||||
# @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
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [912ae422]
|
||||
- @openstapps/core@3.2.0
|
||||
- @openstapps/api@3.2.0
|
||||
- @openstapps/core@4.0.0
|
||||
- @openstapps/api@4.0.0
|
||||
- @openstapps/logger@3.0.0
|
||||
|
||||
## 3.1.1
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@openstapps/minimal-connector",
|
||||
"description": "This is a minimal connector which serves as an example",
|
||||
"version": "3.3.0",
|
||||
"version": "3.2.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "GPL-3.0-only",
|
||||
|
||||
@@ -1,24 +1,13 @@
|
||||
# @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
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [912ae422]
|
||||
- @openstapps/core@3.2.0
|
||||
- @openstapps/api@3.2.0
|
||||
- @openstapps/api-plugin@3.2.0
|
||||
- @openstapps/core@4.0.0
|
||||
- @openstapps/api@4.0.0
|
||||
- @openstapps/api-plugin@4.0.0
|
||||
- @openstapps/core-tools@3.0.0
|
||||
- @openstapps/logger@3.0.0
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@openstapps/minimal-plugin",
|
||||
"description": "Minimal Plugin",
|
||||
"version": "3.3.0",
|
||||
"version": "3.2.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "GPL-3.0-only",
|
||||
|
||||
@@ -48,16 +48,16 @@ const program = new Command()
|
||||
const options = program.opts();
|
||||
|
||||
// create an instance of the PluginClient
|
||||
const pluginClient = new PluginClient(new HttpClient(), options['backendUrl']);
|
||||
const pluginClient = new PluginClient(new HttpClient(), options.backendUrl);
|
||||
|
||||
// create an instance of your plugin
|
||||
const plugin = new MinimalPlugin(
|
||||
// tslint:disable-next-line:no-magic-numbers
|
||||
Number.parseInt(options['port'], 10),
|
||||
options['pluginName'],
|
||||
options['url'],
|
||||
`/${options['routeName']}`,
|
||||
options['backendUrl'],
|
||||
Number.parseInt(options.port, 10),
|
||||
options.pluginName,
|
||||
options.url,
|
||||
`/${options.routeName}`,
|
||||
options.backendUrl,
|
||||
new Converter(path.resolve(__dirname, '..', 'src', 'plugin', 'protocol')), // an instance of the converter. Required
|
||||
// because your requests and response schemas are defined in the plugin. The path should lead to your request and
|
||||
// response interfaces
|
||||
@@ -69,7 +69,7 @@ const plugin = new MinimalPlugin(
|
||||
pluginClient
|
||||
.registerPlugin(plugin)
|
||||
.then(() => {
|
||||
Logger.ok(`Successfully registered plugin '${options['pluginName']}' on /${options['routeName']} .`);
|
||||
Logger.ok(`Successfully registered plugin '${options.pluginName}' on /${options.routeName} .`);
|
||||
})
|
||||
// eslint-disable-next-line unicorn/prefer-top-level-await
|
||||
.catch((error: Error) => {
|
||||
@@ -81,9 +81,7 @@ for (const signal of [`exit`, `SIGINT`, `SIGUSR1`, `SIGUSR2`, `SIGTERM`]) {
|
||||
pluginClient
|
||||
.unregisterPlugin(plugin)
|
||||
.then(() => {
|
||||
Logger.ok(
|
||||
`Successfully unregistered plugin '${options['pluginName']}' from /${options['routeName']} .`,
|
||||
);
|
||||
Logger.ok(`Successfully unregistered plugin '${options.pluginName}' from /${options.routeName} .`);
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
throw error;
|
||||
|
||||
12
flake.lock
generated
12
flake.lock
generated
@@ -5,11 +5,11 @@
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709126324,
|
||||
"narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=",
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "d465f4819400de7c8d874d50b982301f28a84605",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -20,11 +20,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1709747860,
|
||||
"narHash": "sha256-RT4zuBy579m+l8VyIQFOR66WXfcs4g1jntZUHjh6eoI=",
|
||||
"lastModified": 1717112898,
|
||||
"narHash": "sha256-7R2ZvOnvd9h8fDd65p0JnB7wXfUvreox3xFdYWd1BnY=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "58ae79ea707579c40102ddf62d84b902a987c58b",
|
||||
"rev": "6132b0f6e344ce2fe34fc051b72fb46e34f668e0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
141
flake.nix
141
flake.nix
@@ -4,68 +4,85 @@
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
}: let
|
||||
aapt2buildToolsVersion = "33.0.2";
|
||||
in
|
||||
flake-utils.lib.eachDefaultSystem (system: let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [
|
||||
(final: prev: rec {
|
||||
fontMin = prev.python311.withPackages (ps: with ps; [brotli fonttools] ++ (with fonttools.optional-dependencies; [woff]));
|
||||
android = prev.androidenv.composeAndroidPackages {
|
||||
buildToolsVersions = ["30.0.3" aapt2buildToolsVersion];
|
||||
platformVersions = ["33"];
|
||||
};
|
||||
cypress = prev.cypress.overrideAttrs (cyPrev: rec {
|
||||
version = "13.2.0";
|
||||
src = prev.fetchzip {
|
||||
url = "https://cdn.cypress.io/desktop/${version}/linux-x64/cypress.zip";
|
||||
hash = "sha256-9o0nprGcJhudS1LNm+T7Vf0Dwd1RBauYKI+w1FBQ3ZM=";
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
}:
|
||||
let
|
||||
aapt2buildToolsVersion = "33.0.2";
|
||||
in
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [
|
||||
(final: prev: rec {
|
||||
fontMin = prev.python311.withPackages (
|
||||
ps:
|
||||
with ps;
|
||||
[
|
||||
brotli
|
||||
fonttools
|
||||
]
|
||||
++ (with fonttools.optional-dependencies; [ woff ])
|
||||
);
|
||||
android = prev.androidenv.composeAndroidPackages {
|
||||
buildToolsVersions = [
|
||||
"30.0.3"
|
||||
aapt2buildToolsVersion
|
||||
];
|
||||
platformVersions = [ "33" ];
|
||||
};
|
||||
});
|
||||
nodejs = prev.nodejs_18;
|
||||
})
|
||||
];
|
||||
config = {
|
||||
allowUnfree = true;
|
||||
android_sdk.accept_license = true;
|
||||
cypress = prev.cypress.overrideAttrs (cyPrev: rec {
|
||||
version = "13.2.0";
|
||||
src = prev.fetchzip {
|
||||
url = "https://cdn.cypress.io/desktop/${version}/linux-x64/cypress.zip";
|
||||
hash = "sha256-9o0nprGcJhudS1LNm+T7Vf0Dwd1RBauYKI+w1FBQ3ZM=";
|
||||
};
|
||||
});
|
||||
nodejs = prev.nodejs_22;
|
||||
})
|
||||
];
|
||||
config = {
|
||||
allowUnfree = true;
|
||||
android_sdk.accept_license = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
androidFhs = pkgs.buildFHSUserEnv {
|
||||
name = "android-env";
|
||||
targetPkgs = pkgs: with pkgs; [];
|
||||
runScript = "bash";
|
||||
profile = ''
|
||||
export ALLOW_NINJA_ENV=true
|
||||
export USE_CCACHE=1
|
||||
export LD_LIBRARY_PATH=/usr/lib:/usr/lib32
|
||||
'';
|
||||
};
|
||||
in {
|
||||
devShell = pkgs.mkShell rec {
|
||||
nativeBuildInputs = [androidFhs];
|
||||
buildInputs = with pkgs; [
|
||||
nodejs
|
||||
corepack
|
||||
# tools
|
||||
curl
|
||||
jq
|
||||
fontMin
|
||||
cypress
|
||||
# android
|
||||
jdk17
|
||||
android.androidsdk
|
||||
];
|
||||
ANDROID_JAVA_HOME = "${pkgs.jdk.home}";
|
||||
ANDROID_SDK_ROOT = "${pkgs.android.androidsdk}/libexec/android-sdk";
|
||||
GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${ANDROID_SDK_ROOT}/build-tools/${aapt2buildToolsVersion}/aapt2";
|
||||
CYPRESS_INSTALL_BINARY = "0";
|
||||
CYPRESS_RUN_BINARY = "${pkgs.cypress}/bin/Cypress";
|
||||
};
|
||||
});
|
||||
androidFhs = pkgs.buildFHSUserEnv {
|
||||
name = "android-env";
|
||||
targetPkgs = pkgs: with pkgs; [ ];
|
||||
runScript = "bash";
|
||||
profile = ''
|
||||
export ALLOW_NINJA_ENV=true
|
||||
export USE_CCACHE=1
|
||||
export LD_LIBRARY_PATH=/usr/lib:/usr/lib32
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
devShell = pkgs.mkShell rec {
|
||||
nativeBuildInputs = [ androidFhs ];
|
||||
buildInputs = with pkgs; [
|
||||
nodejs
|
||||
corepack
|
||||
# tools
|
||||
curl
|
||||
jq
|
||||
fontMin
|
||||
cypress
|
||||
# android
|
||||
jdk17
|
||||
android.androidsdk
|
||||
];
|
||||
ANDROID_JAVA_HOME = "${pkgs.jdk.home}";
|
||||
ANDROID_SDK_ROOT = "${pkgs.android.androidsdk}/libexec/android-sdk";
|
||||
GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${ANDROID_SDK_ROOT}/build-tools/${aapt2buildToolsVersion}/aapt2";
|
||||
CYPRESS_INSTALL_BINARY = "0";
|
||||
CYPRESS_RUN_BINARY = "${pkgs.cypress}/bin/Cypress";
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
# @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
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -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:
|
||||
|
||||
```sh
|
||||
```
|
||||
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.
|
||||
|
||||
```sh
|
||||
```
|
||||
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.
|
||||
|
||||
```sh
|
||||
```
|
||||
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.
|
||||
|
||||
```sh
|
||||
```
|
||||
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?
|
||||
|
||||
```sh
|
||||
```
|
||||
npm run build
|
||||
npm run start
|
||||
```
|
||||
@@ -86,98 +86,52 @@ addToIonicDB(
|
||||
|
||||
You'll need to run _Chromium_ using
|
||||
|
||||
```sh
|
||||
```shell
|
||||
pnpm chromium:no-cors
|
||||
```
|
||||
|
||||
### Help, I can't log in!
|
||||
|
||||
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
|
||||
|
||||
On the web you can circumvent this locally by using the `:virtual-host` scripts:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
# Start the dev server on mobile.app.uni-frankfurt.de
|
||||
pnpm start:virtual-host
|
||||
# Run chromium with flags that redirect mobile.app.uni-frankfurt.de to localhost:8100
|
||||
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
|
||||
|
||||
Install the npm packages needed for running the app (as for any other node project which uses npm):
|
||||
|
||||
```sh
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
Check the code for linter issues:
|
||||
|
||||
```sh
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
Automatically fix linter issues (those where autofix is possible):
|
||||
|
||||
```sh
|
||||
```
|
||||
npm run lint:fix
|
||||
```
|
||||
|
||||
Build the app (transpile etc.):
|
||||
|
||||
```sh
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
Open the app in the browser:
|
||||
|
||||
```sh
|
||||
```
|
||||
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):
|
||||
|
||||
```sh
|
||||
```
|
||||
npm run build # if needed
|
||||
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
|
||||
@@ -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):
|
||||
|
||||
```sh
|
||||
```
|
||||
npm run docker:build:android
|
||||
```
|
||||
|
||||
@@ -215,13 +169,13 @@ The mentioned `docker:*:android` npm commands are executed in a docker container
|
||||
|
||||
Execute unit tests:
|
||||
|
||||
```sh
|
||||
```
|
||||
npm test
|
||||
```
|
||||
|
||||
Execute e2e tests:
|
||||
|
||||
```sh
|
||||
```
|
||||
npm run e2e
|
||||
```
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ android {
|
||||
|
||||
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
|
||||
dependencies {
|
||||
implementation project(':capacitor-community-screen-brightness')
|
||||
implementation project(':capacitor-app')
|
||||
implementation project(':capacitor-browser')
|
||||
implementation project(':capacitor-clipboard')
|
||||
@@ -22,7 +21,6 @@ dependencies {
|
||||
implementation project(':capacitor-local-notifications')
|
||||
implementation project(':capacitor-network')
|
||||
implementation project(':capacitor-preferences')
|
||||
implementation project(':capacitor-screen-orientation')
|
||||
implementation project(':capacitor-share')
|
||||
implementation project(':capacitor-splash-screen')
|
||||
implementation project(':transistorsoft-capacitor-background-fetch')
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
[
|
||||
{
|
||||
"pkg": "@capacitor-community/screen-brightness",
|
||||
"classpath": "com.elylucas.capscreenbrightness.ScreenBrightnessPlugin"
|
||||
},
|
||||
{
|
||||
"pkg": "@capacitor/app",
|
||||
"classpath": "com.capacitorjs.plugins.app.AppPlugin"
|
||||
@@ -51,10 +47,6 @@
|
||||
"pkg": "@capacitor/preferences",
|
||||
"classpath": "com.capacitorjs.plugins.preferences.PreferencesPlugin"
|
||||
},
|
||||
{
|
||||
"pkg": "@capacitor/screen-orientation",
|
||||
"classpath": "com.capacitorjs.plugins.screenorientation.ScreenOrientationPlugin"
|
||||
},
|
||||
{
|
||||
"pkg": "@capacitor/share",
|
||||
"classpath": "com.capacitorjs.plugins.share.SharePlugin"
|
||||
|
||||
@@ -1,57 +1,51 @@
|
||||
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
|
||||
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')
|
||||
|
||||
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')
|
||||
project(':capacitor-android').projectDir = new File('../../../node_modules/.pnpm/@capacitor+android@5.5.0_@capacitor+core@5.5.0/node_modules/@capacitor/android/capacitor')
|
||||
|
||||
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@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/app/android')
|
||||
|
||||
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@5.1.0_@capacitor+core@5.5.0/node_modules/@capacitor/browser/android')
|
||||
|
||||
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@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/clipboard/android')
|
||||
|
||||
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@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/device/android')
|
||||
|
||||
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@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/dialog/android')
|
||||
|
||||
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@5.1.4_@capacitor+core@5.5.0/node_modules/@capacitor/filesystem/android')
|
||||
|
||||
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@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/geolocation/android')
|
||||
|
||||
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@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/haptics/android')
|
||||
|
||||
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@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/keyboard/android')
|
||||
|
||||
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@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/local-notifications/android')
|
||||
|
||||
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@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/network/android')
|
||||
|
||||
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')
|
||||
|
||||
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')
|
||||
project(':capacitor-preferences').projectDir = new File('../../../node_modules/.pnpm/@capacitor+preferences@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/preferences/android')
|
||||
|
||||
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@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/share/android')
|
||||
|
||||
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@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/splash-screen/android')
|
||||
|
||||
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@5.1.1_@capacitor+core@5.5.0/node_modules/@transistorsoft/capacitor-background-fetch/android')
|
||||
|
||||
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.9.0_@capacitor+core@5.5.0/node_modules/capacitor-secure-storage-plugin/android')
|
||||
|
||||
@@ -12,9 +12,8 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @type {import('./scripts/icon-config').IconConfig}
|
||||
*/
|
||||
|
||||
/** @type {import('./scripts/icon-config').IconConfig} */
|
||||
const config = {
|
||||
inputPath: 'node_modules/material-symbols/material-symbols-rounded.woff2',
|
||||
outputPath: 'src/assets/icons.min.woff2',
|
||||
|
||||
@@ -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@5.5.0_@capacitor+core@5.5.0/node_modules/@capacitor/ios/scripts/pods_helpers'
|
||||
|
||||
platform :ios, '13.0'
|
||||
use_frameworks!
|
||||
@@ -9,26 +9,24 @@ use_frameworks!
|
||||
install! 'cocoapods', :disable_input_output_paths => true
|
||||
|
||||
def capacitor_pods
|
||||
pod 'Capacitor', :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@5.7.3_@capacitor+core@5.7.3/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@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/app'
|
||||
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@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/clipboard'
|
||||
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@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/dialog'
|
||||
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@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/geolocation'
|
||||
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@5.0.8_@capacitor+core@5.7.3/node_modules/@capacitor/keyboard'
|
||||
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@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/network'
|
||||
pod 'CapacitorPreferences', :path => '../../../../node_modules/.pnpm/@capacitor+preferences@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/preferences'
|
||||
pod 'CapacitorScreenOrientation', :path => '../../../../node_modules/.pnpm/@capacitor+screen-orientation@6.0.0_@capacitor+core@5.7.3/node_modules/@capacitor/screen-orientation'
|
||||
pod 'CapacitorShare', :path => '../../../../node_modules/.pnpm/@capacitor+share@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/share'
|
||||
pod 'CapacitorSplashScreen', :path => '../../../../node_modules/.pnpm/@capacitor+splash-screen@5.0.7_@capacitor+core@5.7.3/node_modules/@capacitor/splash-screen'
|
||||
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 'Capacitor', :path => '../../../../node_modules/.pnpm/@capacitor+ios@5.5.0_@capacitor+core@5.5.0/node_modules/@capacitor/ios'
|
||||
pod 'CapacitorCordova', :path => '../../../../node_modules/.pnpm/@capacitor+ios@5.5.0_@capacitor+core@5.5.0/node_modules/@capacitor/ios'
|
||||
pod 'CapacitorApp', :path => '../../../../node_modules/.pnpm/@capacitor+app@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/app'
|
||||
pod 'CapacitorBrowser', :path => '../../../../node_modules/.pnpm/@capacitor+browser@5.1.0_@capacitor+core@5.5.0/node_modules/@capacitor/browser'
|
||||
pod 'CapacitorClipboard', :path => '../../../../node_modules/.pnpm/@capacitor+clipboard@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/clipboard'
|
||||
pod 'CapacitorDevice', :path => '../../../../node_modules/.pnpm/@capacitor+device@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/device'
|
||||
pod 'CapacitorDialog', :path => '../../../../node_modules/.pnpm/@capacitor+dialog@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/dialog'
|
||||
pod 'CapacitorFilesystem', :path => '../../../../node_modules/.pnpm/@capacitor+filesystem@5.1.4_@capacitor+core@5.5.0/node_modules/@capacitor/filesystem'
|
||||
pod 'CapacitorGeolocation', :path => '../../../../node_modules/.pnpm/@capacitor+geolocation@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/geolocation'
|
||||
pod 'CapacitorHaptics', :path => '../../../../node_modules/.pnpm/@capacitor+haptics@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/haptics'
|
||||
pod 'CapacitorKeyboard', :path => '../../../../node_modules/.pnpm/@capacitor+keyboard@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/keyboard'
|
||||
pod 'CapacitorLocalNotifications', :path => '../../../../node_modules/.pnpm/@capacitor+local-notifications@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/local-notifications'
|
||||
pod 'CapacitorNetwork', :path => '../../../../node_modules/.pnpm/@capacitor+network@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/network'
|
||||
pod 'CapacitorPreferences', :path => '../../../../node_modules/.pnpm/@capacitor+preferences@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/preferences'
|
||||
pod 'CapacitorShare', :path => '../../../../node_modules/.pnpm/@capacitor+share@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/share'
|
||||
pod 'CapacitorSplashScreen', :path => '../../../../node_modules/.pnpm/@capacitor+splash-screen@5.0.6_@capacitor+core@5.5.0/node_modules/@capacitor/splash-screen'
|
||||
pod 'TransistorsoftCapacitorBackgroundFetch', :path => '../../../../node_modules/.pnpm/@transistorsoft+capacitor-background-fetch@5.1.1_@capacitor+core@5.5.0/node_modules/@transistorsoft/capacitor-background-fetch'
|
||||
pod 'CapacitorSecureStoragePlugin', :path => '../../../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@5.5.0/node_modules/capacitor-secure-storage-plugin'
|
||||
pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'
|
||||
end
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@openstapps/app",
|
||||
"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,
|
||||
"license": "GPL-3.0-only",
|
||||
"author": "Karl-Philipp Wulfert <krlwlfrt@gmail.com>",
|
||||
@@ -61,7 +61,6 @@
|
||||
"@angular/router": "17.3.0",
|
||||
"@awesome-cordova-plugins/calendar": "6.6.0",
|
||||
"@awesome-cordova-plugins/core": "6.6.0",
|
||||
"@capacitor-community/screen-brightness": "6.0.0",
|
||||
"@capacitor/app": "5.0.7",
|
||||
"@capacitor/browser": "5.2.0",
|
||||
"@capacitor/clipboard": "5.0.7",
|
||||
@@ -75,7 +74,6 @@
|
||||
"@capacitor/local-notifications": "5.0.7",
|
||||
"@capacitor/network": "5.0.7",
|
||||
"@capacitor/preferences": "5.0.7",
|
||||
"@capacitor/screen-orientation": "6.0.0",
|
||||
"@capacitor/share": "5.0.7",
|
||||
"@capacitor/splash-screen": "5.0.7",
|
||||
"@ionic-native/core": "5.36.0",
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
@@ -12,6 +12,24 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
ion-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 {
|
||||
height: 14px;
|
||||
|
||||
@@ -24,17 +24,13 @@ import {
|
||||
} from './errors';
|
||||
import {NGXLogger} from 'ngx-logger';
|
||||
import {sampleIndexResponse} from '../../_helpers/data/sample-configuration';
|
||||
import {BehaviorSubject} from 'rxjs';
|
||||
import {InternetConnectionService} from '../../util/internet-connection.service';
|
||||
|
||||
describe('ConfigProvider', () => {
|
||||
let internetConnectionServiceMock: {offline$: BehaviorSubject<boolean>};
|
||||
let configProvider: ConfigProvider;
|
||||
let storageProviderSpy: jasmine.SpyObj<StorageProvider>;
|
||||
let ngxLogger: jasmine.SpyObj<NGXLogger>;
|
||||
|
||||
beforeEach(() => {
|
||||
internetConnectionServiceMock = {offline$: new BehaviorSubject<boolean>(false)};
|
||||
storageProviderSpy = jasmine.createSpyObj('StorageProvider', ['init', 'get', 'has', 'put']);
|
||||
const webHttpClientMethodSpy = jasmine.createSpyObj('StAppsWebHttpClient', ['request']);
|
||||
ngxLogger = jasmine.createSpyObj('NGXLogger', ['log', 'error', 'warn']);
|
||||
@@ -55,10 +51,6 @@ describe('ConfigProvider', () => {
|
||||
provide: NGXLogger,
|
||||
useValue: ngxLogger,
|
||||
},
|
||||
{
|
||||
provide: InternetConnectionService,
|
||||
useValue: internetConnectionServiceMock,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -83,22 +75,6 @@ describe('ConfigProvider', () => {
|
||||
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 () => {
|
||||
storageProviderSpy.has.and.returnValue(Promise.resolve(false));
|
||||
spyOn(configProvider.client, 'handshake').and.returnValue(Promise.resolve(sampleIndexResponse));
|
||||
|
||||
@@ -27,8 +27,6 @@ import {
|
||||
SavedConfigNotAvailable,
|
||||
WrongConfigVersionInStorage,
|
||||
} from './errors';
|
||||
import {InternetConnectionService} from '../../util/internet-connection.service';
|
||||
import {firstValueFrom} from 'rxjs';
|
||||
|
||||
/**
|
||||
* Key to store config in storage module
|
||||
@@ -74,7 +72,6 @@ export class ConfigProvider {
|
||||
private readonly storageProvider: StorageProvider,
|
||||
swHttpClient: StAppsWebHttpClient,
|
||||
private readonly logger: NGXLogger,
|
||||
private readonly internetConnectionService: InternetConnectionService,
|
||||
) {
|
||||
console.log('config init');
|
||||
this.client = new Client(swHttpClient, environment.backend_url, environment.backend_version);
|
||||
@@ -85,15 +82,9 @@ export class ConfigProvider {
|
||||
*/
|
||||
async fetch(): Promise<SCIndexResponse> {
|
||||
try {
|
||||
const isOffline = await firstValueFrom(this.internetConnectionService.offline$);
|
||||
if (isOffline) {
|
||||
throw new Error('Device is offline.');
|
||||
} else {
|
||||
return await this.client.handshake(this.scVersion);
|
||||
}
|
||||
} catch (error) {
|
||||
const error_ = error instanceof Error ? new ConfigFetchError(error.message) : new ConfigFetchError();
|
||||
throw error_;
|
||||
return await this.client.handshake(this.scVersion);
|
||||
} catch {
|
||||
throw new ConfigFetchError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,8 @@ import {AppError} from '../../_helpers/errors';
|
||||
* Error that is thrown when fetching from backend fails
|
||||
*/
|
||||
export class ConfigFetchError extends AppError {
|
||||
constructor(reason?: string) {
|
||||
const defaultMessage = 'App configuration could not be fetched!';
|
||||
const message = reason ? `${defaultMessage} ${reason}` : defaultMessage;
|
||||
super('ConfigFetchError', message);
|
||||
constructor() {
|
||||
super('ConfigFetchError', 'App configuration could not be fetched!');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,9 +33,9 @@ ion-item {
|
||||
margin: var(--spacing-sm);
|
||||
|
||||
ion-thumbnail {
|
||||
--size: 36px;
|
||||
--ion-margin: var(--spacing-xs);
|
||||
|
||||
margin: 0;
|
||||
margin-block: auto;
|
||||
margin-inline: var(--spacing-md);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export class LibraryAccountPageComponent {
|
||||
constructor(private readonly libraryAccountService: LibraryAccountService) {}
|
||||
|
||||
async ionViewWillEnter(): Promise<void> {
|
||||
const patron = await this.libraryAccountService.getPatron();
|
||||
const patron = await this.libraryAccountService.getProfile();
|
||||
this.name = patron?.name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,6 @@ export class CheckedOutPageComponent {
|
||||
|
||||
async fetchItems() {
|
||||
try {
|
||||
// Prepare patron (status) for the items
|
||||
await this.libraryAccountService.getPatron();
|
||||
this.checkedOutItems = undefined;
|
||||
this.checkedOutItems = await this.libraryAccountService.getFilteredItems([PAIADocumentStatus.Held]);
|
||||
} catch {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
@for (checkedOutItem of checkedOutItems; track checkedOutItem) {
|
||||
<stapps-paia-item
|
||||
[item]="checkedOutItem"
|
||||
[propertiesToShow]="['label', 'renewals', 'duedate']"
|
||||
[propertiesToShow]="['label', 'renewals', 'endtime']"
|
||||
(documentAction)="onDocumentAction($event)"
|
||||
listName="checked_out"
|
||||
>
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
*/
|
||||
|
||||
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
||||
import {DocumentAction, PAIADocument, PAIADocumentStatus, PAIADocumentVisualStatus} from '../../../types';
|
||||
import {LibraryAccountService} from '../../library-account.service';
|
||||
import {DocumentAction, PAIADocument} from '../../../types';
|
||||
|
||||
@Component({
|
||||
selector: 'stapps-paia-item',
|
||||
@@ -23,24 +22,7 @@ import {LibraryAccountService} from '../../library-account.service';
|
||||
styleUrls: ['./paiaitem.scss'],
|
||||
})
|
||||
export class PAIAItemComponent {
|
||||
private _item: PAIADocument;
|
||||
|
||||
renewable: boolean;
|
||||
|
||||
visualStatus?: PAIADocumentVisualStatus;
|
||||
|
||||
constructor(private readonly libraryAccountService: LibraryAccountService) {}
|
||||
|
||||
@Input()
|
||||
set item(value: PAIADocument) {
|
||||
this._item = value;
|
||||
void this.setRenewable();
|
||||
this.visualStatus = this.getVisualStatus(Number(this.item.status));
|
||||
}
|
||||
|
||||
get item(): PAIADocument {
|
||||
return this._item;
|
||||
}
|
||||
@Input() item: PAIADocument;
|
||||
|
||||
@Input()
|
||||
propertiesToShow: (keyof PAIADocument)[];
|
||||
@@ -54,23 +36,4 @@ export class PAIAItemComponent {
|
||||
async onClick(action: DocumentAction['action']) {
|
||||
this.documentAction.emit({doc: this.item, action});
|
||||
}
|
||||
|
||||
private async setRenewable() {
|
||||
const isActive = await this.libraryAccountService.isActivePatron();
|
||||
this.renewable = isActive && Number(this.item.status) === PAIADocumentStatus.Held;
|
||||
}
|
||||
|
||||
private getVisualStatus(status: PAIADocumentStatus): PAIADocumentVisualStatus | undefined {
|
||||
switch (status) {
|
||||
case PAIADocumentStatus.Ordered: {
|
||||
return {color: 'warning', status: status, statusText: 'ordered'};
|
||||
}
|
||||
case PAIADocumentStatus.Provided: {
|
||||
return {color: 'success', status: status, statusText: 'ready'};
|
||||
}
|
||||
default: {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,22 +14,16 @@
|
||||
-->
|
||||
|
||||
<ion-item>
|
||||
<!-- TODO: text not selectable in Chrome, bugfix needed https://github.com/ionic-team/ionic-framework/issues/24956 -->
|
||||
<ion-label class="ion-text-wrap">
|
||||
@if (item.about) {
|
||||
<h2 class="name">
|
||||
@if (visualStatus) {
|
||||
<ion-badge [color]="visualStatus.color" slot="start">
|
||||
{{ 'library.account.pages' + '.' + listName + '.' + visualStatus.statusText | translate }}
|
||||
</ion-badge>
|
||||
}
|
||||
{{ item.about }}
|
||||
</h2>
|
||||
<h2 class="name">{{ item.about }}</h2>
|
||||
}
|
||||
@for (property of propertiesToShow; track property) {
|
||||
@if (item[property]) {
|
||||
<p>
|
||||
{{ 'library.account.pages' + '.' + listName + '.' + 'labels' + '.' + property | translate }}:
|
||||
@if (!['starttime', 'duedate'].includes(property)) {
|
||||
@if (!['endtime', 'duedate'].includes(property)) {
|
||||
{{ item[property] }}
|
||||
} @else {
|
||||
{{ $any(item[property]) | amDateFormat: 'll' }}
|
||||
@@ -46,7 +40,7 @@
|
||||
<!-- >-->
|
||||
<!-- {{ 'library.account.actions.cancel.header' | translate }}</ion-button-->
|
||||
<!-- >-->
|
||||
@if (renewable && item.canrenew) {
|
||||
@if (item.canrenew) {
|
||||
<ion-button color="primary" (click)="onClick('renew')">
|
||||
{{ 'library.account.actions.renew.header' | translate }}</ion-button
|
||||
>
|
||||
|
||||
@@ -12,6 +12,3 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
ion-badge {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
@@ -45,8 +45,6 @@ export class HoldsPageComponent {
|
||||
? [PAIADocumentStatus.Reserved]
|
||||
: [PAIADocumentStatus.Ordered, PAIADocumentStatus.Provided];
|
||||
try {
|
||||
// Prepare patron (status) for the items
|
||||
await this.libraryAccountService.getPatron();
|
||||
this.paiaDocuments = await this.libraryAccountService.getFilteredItems(itemsStatus);
|
||||
} catch {
|
||||
await this.libraryAccountService.handleError();
|
||||
|
||||
@@ -35,20 +35,30 @@
|
||||
@switch (activeSegment) {
|
||||
@case ('orders') {
|
||||
@for (hold of paiaDocuments; track hold) {
|
||||
<stapps-paia-item
|
||||
[item]="hold"
|
||||
[propertiesToShow]="['label', 'storage']"
|
||||
(documentAction)="onDocumentAction($event)"
|
||||
listName="holds"
|
||||
>
|
||||
</stapps-paia-item>
|
||||
@if (toNumber(hold.status) === paiaDocumentStatus.Provided) {
|
||||
<stapps-paia-item
|
||||
[item]="hold"
|
||||
[propertiesToShow]="['label', 'storage']"
|
||||
(documentAction)="onDocumentAction($event)"
|
||||
listName="holds"
|
||||
>
|
||||
</stapps-paia-item>
|
||||
} @else {
|
||||
<stapps-paia-item
|
||||
[item]="hold"
|
||||
[propertiesToShow]="['label']"
|
||||
(documentAction)="onDocumentAction($event)"
|
||||
listName="holds"
|
||||
>
|
||||
</stapps-paia-item>
|
||||
}
|
||||
}
|
||||
}
|
||||
@case ('reservations') {
|
||||
@for (hold of paiaDocuments; track hold) {
|
||||
<stapps-paia-item
|
||||
[item]="hold"
|
||||
[propertiesToShow]="['label', 'starttime', 'storage', 'queue']"
|
||||
[propertiesToShow]="['label']"
|
||||
(documentAction)="onDocumentAction($event)"
|
||||
listName="holds"
|
||||
>
|
||||
|
||||
@@ -20,15 +20,7 @@ import {
|
||||
SCFeatureConfiguration,
|
||||
SCFeatureConfigurationExtern,
|
||||
} from '@openstapps/core';
|
||||
import {
|
||||
DocumentAction,
|
||||
PAIADocument,
|
||||
PAIADocumentStatus,
|
||||
PAIAFees,
|
||||
PAIAItems,
|
||||
PAIAPatron,
|
||||
PAIAPatronStatus,
|
||||
} from '../types';
|
||||
import {DocumentAction, PAIADocument, PAIADocumentStatus, PAIAFees, PAIAItems, PAIAPatron} from '../types';
|
||||
import {HebisDataProvider} from '../../hebis/hebis-data.provider';
|
||||
import {PAIATokenResponse} from '../../auth/paia/paia-token-response';
|
||||
import {AuthHelperService} from '../../auth/auth-helper.service';
|
||||
@@ -51,11 +43,6 @@ export class LibraryAccountService {
|
||||
*/
|
||||
authType: SCAuthorizationProviderType;
|
||||
|
||||
/**
|
||||
* Account (Patron) status
|
||||
*/
|
||||
private status?: PAIAPatronStatus;
|
||||
|
||||
constructor(
|
||||
protected requestor: Requestor = new JQueryRequestor(),
|
||||
private readonly hebisDataProvider: HebisDataProvider,
|
||||
@@ -73,23 +60,12 @@ export class LibraryAccountService {
|
||||
this.authType = config.authProvider as SCAuthorizationProviderType;
|
||||
}
|
||||
|
||||
async getPatron() {
|
||||
const patronId = ((await this.getValidToken()) as PAIATokenResponse).patron;
|
||||
const patron = {
|
||||
async getProfile() {
|
||||
const patron = ((await this.getValidToken()) as PAIATokenResponse).patron;
|
||||
return {
|
||||
...(await this.performRequest<PAIAPatron>(`${this.baseUrl}/{patron}`)),
|
||||
id: patronId,
|
||||
id: patron,
|
||||
} 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() {
|
||||
|
||||
@@ -25,19 +25,15 @@ import {PAIAPatron} from '../../types';
|
||||
export class ProfilePageComponent {
|
||||
patron?: PAIAPatron;
|
||||
|
||||
propertiesToShow: (keyof PAIAPatron)[] = ['id', 'name', 'email', 'address', 'expires'];
|
||||
propertiesToShow: (keyof PAIAPatron)[] = ['id', 'name', 'email', 'address', 'expires', 'note'];
|
||||
|
||||
constructor(private readonly libraryAccountService: LibraryAccountService) {}
|
||||
|
||||
async ionViewWillEnter(): Promise<void> {
|
||||
try {
|
||||
this.patron = await this.libraryAccountService.getPatron();
|
||||
this.patron = await this.libraryAccountService.getProfile();
|
||||
} catch {
|
||||
await this.libraryAccountService.handleError();
|
||||
}
|
||||
}
|
||||
|
||||
isUnlimitedExpiry(date = ''): boolean {
|
||||
return new Date(date).getFullYear() === 9999;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,11 +36,11 @@
|
||||
@if (!['expires'].includes(property)) {
|
||||
{{ patron[property] }}
|
||||
} @else {
|
||||
@if (isUnlimitedExpiry(patron['expires'])) {
|
||||
@if (patron[property] === '9999-12-31') {
|
||||
{{ 'library.account.pages.profile.values.unlimited' | translate }}
|
||||
} @else {
|
||||
{{ 'library.account.pages.profile.values.expires' | translate }}: {{
|
||||
patron['expires'] | amDateFormat: 'll'
|
||||
patron[property] | amDateFormat: 'll'
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,19 +19,11 @@ export interface PAIAPatron {
|
||||
email?: string;
|
||||
address?: string;
|
||||
expires?: string;
|
||||
status?: PAIAPatronStatus;
|
||||
status?: string;
|
||||
type?: 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
|
||||
* 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;
|
||||
renewals?: string;
|
||||
reminder?: string;
|
||||
starttime?: string;
|
||||
endtime?: string;
|
||||
duedate?: string;
|
||||
cancancel?: boolean;
|
||||
canrenew?: boolean;
|
||||
@@ -88,9 +80,3 @@ export interface DocumentAction {
|
||||
action: 'cancel' | 'renew';
|
||||
doc: PAIADocument;
|
||||
}
|
||||
|
||||
export interface PAIADocumentVisualStatus {
|
||||
color: 'warning' | 'success';
|
||||
status: PAIADocumentStatus;
|
||||
statusText: 'ordered' | 'ready';
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {Directive, Input, Host} from '@angular/core';
|
||||
import {MapComponent} from '@maplibre/ngx-maplibre-gl';
|
||||
import {environment} from '../../../environments/environment';
|
||||
|
||||
@Directive({
|
||||
selector: 'mgl-map[styleName]',
|
||||
@@ -11,7 +10,7 @@ export class MapStyleDirective {
|
||||
|
||||
@Input()
|
||||
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) {
|
||||
this.map.mapInstance.setStyle(style);
|
||||
} else {
|
||||
|
||||
@@ -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 {FullScreenImageDirective} from '../../util/full-screen-image.directive';
|
||||
import {ThingTranslateModule} from '../../translation/thing-translate.module';
|
||||
import {AsyncPipe, TitleCasePipe} from '@angular/common';
|
||||
import {InRangeNowPipe, ToDateRangePipe} from '../../util/in-range.pipe';
|
||||
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({
|
||||
selector: 'stapps-id-card',
|
||||
@@ -16,130 +12,17 @@ import {iosDuration, iosEasing, mdDuration, mdEasing} from 'src/app/animation/ea
|
||||
styleUrls: ['id-card.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [ThingTranslateModule, InRangeNowPipe, ToDateRangePipe, AsyncPipe, TranslateModule, TitleCasePipe],
|
||||
hostDirectives: [FullScreenImageDirective],
|
||||
imports: [
|
||||
FullScreenImageDirective,
|
||||
ThingTranslateModule,
|
||||
InRangeNowPipe,
|
||||
ToDateRangePipe,
|
||||
AsyncPipe,
|
||||
TranslateModule,
|
||||
TitleCasePipe,
|
||||
],
|
||||
})
|
||||
export class IdCardComponent {
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
<img
|
||||
[src]="item.image"
|
||||
[alt]="'name' | thingTranslate: item"
|
||||
draggable="false"
|
||||
(click)="presentFullscreen()"
|
||||
/>
|
||||
<img [src]="item.image" [alt]="'name' | thingTranslate: item" draggable="false" />
|
||||
@if (item.validity && (item.validity | toDateRange | isInRangeNow | async) === false) {
|
||||
<div class="expired">{{ 'profile.userInfo.expired' | translate | titlecase }}</div>
|
||||
}
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:host:fullscreen {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 3mm;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import {IonicModule} from '@ionic/angular';
|
||||
import {AsyncPipe, TitleCasePipe} from '@angular/common';
|
||||
import {ThingTranslateModule} from '../../translation/thing-translate.module';
|
||||
import {UtilModule} from '../../util/util.module';
|
||||
import {FullScreenImageDirective} from '../../util/full-screen-image.directive';
|
||||
import {IdCardComponent} from './id-card.component';
|
||||
import {TranslateModule} from '@ngx-translate/core';
|
||||
import {Observable} from 'rxjs';
|
||||
@@ -35,6 +36,7 @@ import {Observable} from 'rxjs';
|
||||
AsyncPipe,
|
||||
ThingTranslateModule,
|
||||
UtilModule,
|
||||
FullScreenImageDirective,
|
||||
IdCardComponent,
|
||||
TranslateModule,
|
||||
TitleCasePipe,
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import {Injectable} from '@angular/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 {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 {HttpClient} from '@angular/common/http';
|
||||
import {EncryptedStorageProvider} from '../storage/encrypted-storage.provider';
|
||||
|
||||
@Injectable({providedIn: 'root'})
|
||||
export class IdCardsProvider {
|
||||
@@ -13,27 +12,18 @@ export class IdCardsProvider {
|
||||
private authHelper: AuthHelperService,
|
||||
private config: ConfigProvider,
|
||||
private httpClient: HttpClient,
|
||||
private encryptedStorageProvider: EncryptedStorageProvider,
|
||||
) {}
|
||||
|
||||
getIdCards(): Observable<SCIdCard[]> {
|
||||
const feature = this.config.config.app.features.extern?.['idCards'];
|
||||
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(
|
||||
mergeMap(isAuthenticated =>
|
||||
isAuthenticated
|
||||
? feature
|
||||
? storedIdCards.pipe(
|
||||
concatWith(
|
||||
from(auth.getValidToken()).pipe(
|
||||
mergeMap(token => this.fetchIdCards(feature.url, token.accessToken)),
|
||||
catchError(() => storedIdCards),
|
||||
),
|
||||
),
|
||||
? from(auth.getValidToken()).pipe(
|
||||
mergeMap(token => this.fetchIdCards(feature.url, token.accessToken)),
|
||||
)
|
||||
: auth.user$.pipe(
|
||||
filter(user => user !== undefined),
|
||||
@@ -41,20 +31,19 @@ export class IdCardsProvider {
|
||||
mergeMap(user => this.fetchFallbackIdCards(user)),
|
||||
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[]> {
|
||||
return this.httpClient
|
||||
.get<SCIdCard[]>(url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
responseType: 'json',
|
||||
})
|
||||
.pipe(tap(idCards => this.encryptedStorageProvider.set('id-cards', idCards)));
|
||||
return this.httpClient.get<SCIdCard[]>(url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
responseType: 'json',
|
||||
});
|
||||
}
|
||||
|
||||
private fetchFallbackIdCards(user: SCUserConfiguration): Observable<SCIdCard[]> {
|
||||
|
||||
@@ -4,7 +4,6 @@ import {HttpClient} from '@angular/common/http';
|
||||
import {AuthHelperService} from '../auth/auth-helper.service';
|
||||
import {BehaviorSubject, firstValueFrom, of} from 'rxjs';
|
||||
import {SCAuthorizationProviderType} from '@openstapps/core';
|
||||
import {EncryptedStorageProvider} from '../storage/encrypted-storage.provider';
|
||||
|
||||
class FakeAuth {
|
||||
isAuthenticated$ = new BehaviorSubject(false);
|
||||
@@ -17,7 +16,6 @@ describe('IdCards', () => {
|
||||
let configProvider: ConfigProvider;
|
||||
let httpClient: HttpClient;
|
||||
let authHelper: AuthHelperService;
|
||||
let encryptedStorageProvider: EncryptedStorageProvider;
|
||||
let fakeAuth: FakeAuth;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -29,14 +27,10 @@ describe('IdCards', () => {
|
||||
fakeAuth = new FakeAuth();
|
||||
authHelper = jasmine.createSpyObj('AuthHelperService', ['getProvider']);
|
||||
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 () => {
|
||||
const provider = new IdCardsProvider(authHelper, configProvider, httpClient, encryptedStorageProvider);
|
||||
const provider = new IdCardsProvider(authHelper, configProvider, httpClient);
|
||||
expect(await firstValueFrom(provider.getIdCards())).toEqual([]);
|
||||
expect(authHelper.getProvider).toHaveBeenCalledOnceWith('fakeAuth' as SCAuthorizationProviderType);
|
||||
});
|
||||
@@ -45,7 +39,7 @@ describe('IdCards', () => {
|
||||
fakeAuth.isAuthenticated$.next(true);
|
||||
httpClient.get = jasmine.createSpy().and.returnValue(of(['abc']));
|
||||
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(authHelper.getProvider).toHaveBeenCalledOnceWith('fakeAuth' as SCAuthorizationProviderType);
|
||||
// eslint-disable-next-line unicorn/no-null
|
||||
@@ -58,7 +52,7 @@ describe('IdCards', () => {
|
||||
});
|
||||
|
||||
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();
|
||||
expect(await firstValueFrom(observable)).toEqual([]);
|
||||
httpClient.get = jasmine.createSpy().and.returnValue(of(['abc']));
|
||||
|
||||
@@ -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}`),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -336,15 +336,11 @@
|
||||
"title": "Titel",
|
||||
"about": "Mehr Informationen",
|
||||
"label": "Signatur",
|
||||
"starttime": "Vorgemerkt am",
|
||||
"endtime": "Abzuholen bis",
|
||||
"storage": "Abholtheke",
|
||||
"queue": "Position in der Warteschlange"
|
||||
"storage": "Abholbereit"
|
||||
},
|
||||
"holds": "Bestellungen",
|
||||
"reservations": "Vormerkungen",
|
||||
"ordered": "Bestellt",
|
||||
"ready": "Abholbereit"
|
||||
"reservations": "Vormerkungen"
|
||||
},
|
||||
"checked_out": {
|
||||
"title": "Deine Ausleihen",
|
||||
@@ -352,7 +348,7 @@
|
||||
"title": "Titel",
|
||||
"about": "Mehr Informationen",
|
||||
"label": "Signatur",
|
||||
"duedate": "Leihfristende",
|
||||
"endtime": "Leihfristende",
|
||||
"renewals": "Verlängerungen"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -336,15 +336,11 @@
|
||||
"title": "Title",
|
||||
"about": "More information",
|
||||
"label": "Shelfmark",
|
||||
"starttime": "Reserved on",
|
||||
"endtime": "Available for pickup until",
|
||||
"storage": "Pickup counter",
|
||||
"queue": "Position in the queue"
|
||||
"storage": "Available for pickup"
|
||||
},
|
||||
"holds": "orders",
|
||||
"reservations": "reservations",
|
||||
"ordered": "Ordered",
|
||||
"ready": "Ready for pickup"
|
||||
"reservations": "reservations"
|
||||
},
|
||||
"checked_out": {
|
||||
"title": "checked out items",
|
||||
@@ -352,7 +348,7 @@
|
||||
"title": "Title",
|
||||
"about": "More information",
|
||||
"label": "Label",
|
||||
"duedate": "Due date",
|
||||
"endtime": "Due date",
|
||||
"renewals": "Renewals"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@ export const environment = {
|
||||
backend_url: 'https://mobile.server.uni-frankfurt.de',
|
||||
app_host: 'mobile.app.uni-frankfurt.de',
|
||||
custom_url_scheme: 'de.anyschool.app',
|
||||
backend_version: '999.0.0',
|
||||
backend_version: '3.1.0',
|
||||
production: true,
|
||||
};
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ export const environment = {
|
||||
backend_url: 'https://mobile.server.uni-frankfurt.de',
|
||||
app_host: 'mobile.app.uni-frankfurt.de',
|
||||
custom_url_scheme: 'de.anyschool.app',
|
||||
backend_version: '999.0.0',
|
||||
backend_version: '3.1.0',
|
||||
production: false,
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"outDir": "./dist/out-tsc",
|
||||
"declaration": false,
|
||||
"isolatedModules": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"checkJs": false,
|
||||
"allowJs": false,
|
||||
"strictPropertyInitialization": false,
|
||||
"downlevelIteration": true,
|
||||
"importHelpers": true,
|
||||
@@ -19,6 +20,3 @@
|
||||
},
|
||||
"exclude": ["**/*.spec.ts"]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
SCRSUFFIX="_18.x"
|
||||
NODENAME="Node.js 18.x"
|
||||
NODEREPO="node_18.x"
|
||||
SCRSUFFIX="_22.x"
|
||||
NODENAME="Node.js 22.x"
|
||||
NODEREPO="node_22.x"
|
||||
NODEPKG="nodejs"
|
||||
|
||||
print_status() {
|
||||
@@ -94,7 +94,7 @@ node_deprecation_warning() {
|
||||
"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 11.x" ||
|
||||
"X${NODENAME}" == "XNode.js 12.x" ||
|
||||
"X${NODENAME}" == "XNode.js 13.x" ||
|
||||
"X${NODENAME}" == "XNode.js 14.x" ||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
### Set base image
|
||||
FROM cypress/base:18.16.1
|
||||
FROM cypress/base:22.0.0
|
||||
|
||||
USER root
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:18-alpine3.18
|
||||
FROM node:22-alpine
|
||||
|
||||
RUN apk update && apk add git curl jq && mkdir -p /opt
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:18-alpine3.18
|
||||
FROM node:22-alpine
|
||||
|
||||
RUN apk update && apk add git jq curl python3 build-base
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"name": "@openstapps/openstapps",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=18.16",
|
||||
"pnpm": ">=8"
|
||||
"node": "^22.0.0",
|
||||
"pnpm": "^8.15.4"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "dotenv -c -- turbo run build",
|
||||
@@ -13,7 +13,6 @@
|
||||
"deploy": "dotenv -c -- turbo run deploy --concurrency=1",
|
||||
"dev": "dotenv -c -- turbo run dev",
|
||||
"docs": "dotenv -c -- turbo run docs && typedoc && mkdir docs/api && cp packages/core/lib/api-doc.html docs/api/index.html && cp packages/core/lib/openapi.json docs/api/openapi.json && cp -r packages/core/lib/schema docs/api/schema",
|
||||
"docs:serve": "http-server docs -p 8080 -o",
|
||||
"format": "dotenv -c -- turbo run format",
|
||||
"format:fix": "dotenv -c -- turbo run format:fix",
|
||||
"lint": "dotenv -c -- turbo run lint",
|
||||
@@ -35,7 +34,6 @@
|
||||
"deepmerge": "4.3.1",
|
||||
"dotenv-cli": "7.2.1",
|
||||
"glob": "10.3.10",
|
||||
"http-server": "14.1.1",
|
||||
"junit-report-merger": "6.0.3",
|
||||
"prettier": "3.1.1",
|
||||
"syncpack": "12.3.0",
|
||||
|
||||
@@ -1,27 +1,12 @@
|
||||
# @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
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [912ae422]
|
||||
- @openstapps/core@3.2.0
|
||||
- @openstapps/api@3.2.0
|
||||
- @openstapps/core@4.0.0
|
||||
- @openstapps/api@4.0.0
|
||||
- @openstapps/core-tools@3.0.0
|
||||
- @openstapps/logger@3.0.0
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@openstapps/api-cli",
|
||||
"description": "CLI client for @openstapps/api",
|
||||
"version": "3.3.0",
|
||||
"version": "3.2.0",
|
||||
"type": "module",
|
||||
"license": "GPL-3.0-only",
|
||||
"repository": "git@gitlab.com:openstapps/api.git",
|
||||
|
||||
@@ -1,22 +1,12 @@
|
||||
# @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
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [912ae422]
|
||||
- @openstapps/core@3.2.0
|
||||
- @openstapps/api@3.2.0
|
||||
- @openstapps/core@4.0.0
|
||||
- @openstapps/api@4.0.0
|
||||
- @openstapps/core-tools@3.0.0
|
||||
- @openstapps/logger@3.0.0
|
||||
|
||||
|
||||
10
packages/api-plugin/environment.d.ts
vendored
10
packages/api-plugin/environment.d.ts
vendored
@@ -1,10 +0,0 @@
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
NODE_ENV?: string;
|
||||
PORT?: string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@openstapps/api-plugin",
|
||||
"description": "Node.js library to interact with the StApps backend service",
|
||||
"version": "3.3.0",
|
||||
"version": "3.2.0",
|
||||
"type": "module",
|
||||
"license": "GPL-3.0-only",
|
||||
"repository": "git@gitlab.com:openstapps/api.git",
|
||||
|
||||
@@ -175,11 +175,13 @@ export abstract class Plugin {
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
Logger.error(`${bind} requires elevated privileges`);
|
||||
process.exit(1);
|
||||
break;
|
||||
}
|
||||
case 'EADDRINUSE': {
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
Logger.error(`${bind} is already in use`);
|
||||
process.exit(1);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw error;
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
# @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
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [912ae422]
|
||||
- @openstapps/core@3.2.0
|
||||
- @openstapps/core@4.0.0
|
||||
|
||||
## 3.1.1
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@openstapps/api",
|
||||
"description": "Node.js library to interact with the StApps backend service",
|
||||
"version": "3.3.0",
|
||||
"version": "3.2.0",
|
||||
"type": "module",
|
||||
"license": "GPL-3.0-only",
|
||||
"repository": "git@gitlab.com:openstapps/api.git",
|
||||
|
||||
@@ -128,8 +128,8 @@ export class Client {
|
||||
size: 1,
|
||||
});
|
||||
|
||||
if (response.data.length === 1 && response.data[0]!.uid === uid) {
|
||||
return response.data[0]!;
|
||||
if (response.data.length === 1 && response.data[0].uid === uid) {
|
||||
return response.data[0];
|
||||
}
|
||||
|
||||
throw new SCInternalServerErrorResponse(new SCNotFoundErrorResponse(true), true);
|
||||
@@ -220,11 +220,11 @@ export class Client {
|
||||
for (const key of Object.keys(multiSearchRequest)) {
|
||||
const searchRequest = multiSearchRequest[key];
|
||||
|
||||
if (searchRequest?.size === undefined) {
|
||||
if (searchRequest.size === undefined) {
|
||||
preFlightRequest[key] = {
|
||||
...searchRequest,
|
||||
};
|
||||
preFlightRequest[key]!.size = 0;
|
||||
preFlightRequest[key].size = 0;
|
||||
preFlightNecessary = true;
|
||||
}
|
||||
}
|
||||
@@ -245,8 +245,8 @@ export class Client {
|
||||
);
|
||||
|
||||
// set size for multi search requests that were in pre flight request
|
||||
for (const key in preFlightRequest) {
|
||||
returnMultiSearchRequest[key]!.size = preFlightResponse[key]!.pagination.total;
|
||||
for (const key of Object.keys(preFlightRequest)) {
|
||||
returnMultiSearchRequest[key].size = preFlightResponse[key].pagination.total;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@ export class ConnectorClient extends Client {
|
||||
const thingSource = source === undefined ? 'stapps-api' : source;
|
||||
|
||||
// request a new bulk
|
||||
const bulk = await this.bulk(things[0]!.type, thingSource, timeout);
|
||||
const bulk = await this.bulk(things[0].type, thingSource, timeout);
|
||||
|
||||
// add items to the bulk - 5 concurrently
|
||||
await Promise.all(
|
||||
|
||||
@@ -33,7 +33,7 @@ export class ApiError extends Error {
|
||||
/**
|
||||
* Add additional data to the output of the error
|
||||
*/
|
||||
override toString(): string {
|
||||
toString(): string {
|
||||
let string_ = super.toString();
|
||||
|
||||
// add additional data
|
||||
|
||||
@@ -20,7 +20,7 @@ export function groupBy<T>(collection: T[], group: (item: T) => string | undefin
|
||||
return collection.reduce((accumulator: Record<string, T[]>, item) => {
|
||||
const key = group(item) ?? '';
|
||||
accumulator[key] = accumulator[key] ?? [];
|
||||
accumulator[key]!.push(item);
|
||||
accumulator[key].push(item);
|
||||
return accumulator;
|
||||
}, {});
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export function shuffle<T>(array: T[]): T[] {
|
||||
const out = [];
|
||||
|
||||
while (copy.length > 0) {
|
||||
out.push(copy.splice(Math.floor(Math.random() * copy.length), 1)[0]!);
|
||||
out.push(copy.splice(Math.floor(Math.random() * copy.length), 1)[0]);
|
||||
}
|
||||
|
||||
return out;
|
||||
|
||||
@@ -17,5 +17,5 @@
|
||||
* Zip two arrays together.
|
||||
*/
|
||||
export function zip<T, U>(a: T[], b: U[]): [T, U][] {
|
||||
return a.map((_, i) => [a[i]!, b[i]!]);
|
||||
return a.map((_, i) => [a[i], b[i]]);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
# @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
|
||||
|
||||
### Major Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@openstapps/core-tools",
|
||||
"description": "Tools to convert and validate StAppsCore",
|
||||
"version": "3.3.0",
|
||||
"version": "3.0.0",
|
||||
"type": "module",
|
||||
"license": "GPL-3.0-only",
|
||||
"repository": "git@gitlab.com:openstapps/core-tools.git",
|
||||
|
||||
@@ -60,11 +60,11 @@ commander
|
||||
|
||||
// get information about routes
|
||||
const routes = await gatherRouteInformation(sourcePath);
|
||||
routes.sort((a, b) => a.route['urlPath'].localeCompare(b.route['urlPath']));
|
||||
routes.sort((a, b) => a.route.urlPath.localeCompare(b.route.urlPath));
|
||||
|
||||
// change url path parameters to openapi notation
|
||||
for (const routeWithMetaInformation of routes) {
|
||||
routeWithMetaInformation.route['urlPath'] = routeWithMetaInformation.route['urlPath'].replaceAll(
|
||||
routeWithMetaInformation.route.urlPath = routeWithMetaInformation.route.urlPath.replaceAll(
|
||||
/:\w+/g,
|
||||
(match: string) => `{${match.replace(':', '')}}`,
|
||||
);
|
||||
@@ -72,7 +72,7 @@ commander
|
||||
|
||||
// keep openapi tags for routes that actually share url fragments
|
||||
let tagsToKeep = routes.map(routeWithMetaInformation =>
|
||||
capitalize(routeWithMetaInformation.route['urlPath'].split('/')[1]),
|
||||
capitalize(routeWithMetaInformation.route.urlPath.split('/')[1]),
|
||||
);
|
||||
tagsToKeep = tagsToKeep.filter(
|
||||
(element, i, array) => array.indexOf(element) === i && array.lastIndexOf(element) !== i,
|
||||
@@ -83,9 +83,9 @@ commander
|
||||
|
||||
// generate documentation for all routes
|
||||
for (const routeWithMetaInformation of routes) {
|
||||
routeWithMetaInformation.tags = [capitalize(routeWithMetaInformation.route['urlPath'].split('/')[1])];
|
||||
routeWithMetaInformation.tags = [capitalize(routeWithMetaInformation.route.urlPath.split('/')[1])];
|
||||
|
||||
output.paths[routeWithMetaInformation.route['urlPath']] = generateOpenAPIForRoute(
|
||||
output.paths[routeWithMetaInformation.route.urlPath] = generateOpenAPIForRoute(
|
||||
routeWithMetaInformation,
|
||||
path.relative(relativeOutDirectoryPath, outDirectorySchemasPath),
|
||||
tagsToKeep,
|
||||
@@ -165,7 +165,7 @@ commander
|
||||
continue;
|
||||
}
|
||||
|
||||
unexpected = unexpected || errorsPerFile[file]?.some(error => !error.expected) || false;
|
||||
unexpected = unexpected || errorsPerFile[file].some(error => !error.expected);
|
||||
}
|
||||
|
||||
if (relativeReportPath !== undefined) {
|
||||
|
||||
@@ -48,7 +48,7 @@ export async function gatherRouteInformation(path: string): Promise<RouteWithMet
|
||||
// instantiate all errors
|
||||
instantiatedRoute.errors = await Promise.all(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
instantiatedRoute['errorNames'].map(async (error: any) =>
|
||||
instantiatedRoute.errorNames.map(async (error: any) =>
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
Object.assign((await project.instantiateDefinitionByName(error.name)) as object, {
|
||||
name: error.name,
|
||||
@@ -57,10 +57,10 @@ export async function gatherRouteInformation(path: string): Promise<RouteWithMet
|
||||
);
|
||||
instantiatedRoute.responseBodyDescription =
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
|
||||
project.definitions[instantiatedRoute['responseBodyName']]?.comment?.shortSummary!;
|
||||
project.definitions[instantiatedRoute.responseBodyName]?.comment?.shortSummary!;
|
||||
instantiatedRoute.requestBodyDescription =
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
|
||||
project.definitions[instantiatedRoute['requestBodyName']]?.comment?.shortSummary!;
|
||||
project.definitions[instantiatedRoute.requestBodyName]?.comment?.shortSummary!;
|
||||
|
||||
return {
|
||||
description: {
|
||||
@@ -89,7 +89,7 @@ export function generateOpenAPIForRoute(
|
||||
const route = routeWithInfo.route;
|
||||
const openapiPath: OpenAPIV3.PathItemObject = {};
|
||||
|
||||
openapiPath[route['method'].toLowerCase() as OpenAPIV3.HttpMethods] = {
|
||||
openapiPath[route.method.toLowerCase() as OpenAPIV3.HttpMethods] = {
|
||||
summary: capitalize(routeWithInfo.description.shortText?.replace(/(Route to |Route for )/gim, '')),
|
||||
description: routeWithInfo.description.text,
|
||||
requestBody: {
|
||||
@@ -97,7 +97,7 @@ export function generateOpenAPIForRoute(
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: path.join(outDirectorySchemasPath, `${route['requestBodyName']}.json`),
|
||||
$ref: path.join(outDirectorySchemasPath, `${route.requestBodyName}.json`),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -117,21 +117,19 @@ export function generateOpenAPIForRoute(
|
||||
tags: routeWithInfo.tags?.filter(value => tagsToKeep.includes(value)),
|
||||
};
|
||||
|
||||
openapiPath[route['method'].toLowerCase() as OpenAPIV3.HttpMethods]!.responses![
|
||||
route['statusCodeSuccess']
|
||||
] = {
|
||||
openapiPath[route.method.toLowerCase() as OpenAPIV3.HttpMethods]!.responses![route.statusCodeSuccess] = {
|
||||
description: route.responseBodyDescription,
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: path.join(outDirectorySchemasPath, `${route['responseBodyName']}.json`),
|
||||
$ref: path.join(outDirectorySchemasPath, `${route.responseBodyName}.json`),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
for (const error of route.errors) {
|
||||
openapiPath[route['method'].toLowerCase() as OpenAPIV3.HttpMethods]!.responses![error.statusCode] = {
|
||||
openapiPath[route.method.toLowerCase() as OpenAPIV3.HttpMethods]!.responses![error.statusCode] = {
|
||||
description:
|
||||
error.message ?? capitalize(error.name.replaceAll(/([A-Z][a-z])/g, ' $1').replace('SC ', '')),
|
||||
content: {
|
||||
@@ -144,8 +142,8 @@ export function generateOpenAPIForRoute(
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof route['obligatoryParameters'] === 'object') {
|
||||
for (const [parameter, schemaDefinition] of Object.entries(route['obligatoryParameters'])) {
|
||||
if (typeof route.obligatoryParameters === 'object') {
|
||||
for (const [parameter, schemaDefinition] of Object.entries(route.obligatoryParameters)) {
|
||||
const openapiParameter: OpenAPIV3.ParameterObject = {
|
||||
in: 'path',
|
||||
name: parameter,
|
||||
@@ -155,7 +153,7 @@ export function generateOpenAPIForRoute(
|
||||
$ref: `schema/SCSearchResponse.json#/definitions/${schemaDefinition}`,
|
||||
},
|
||||
};
|
||||
openapiPath[route['method'].toLowerCase() as OpenAPIV3.HttpMethods]?.parameters?.push(openapiParameter);
|
||||
openapiPath[route.method.toLowerCase() as OpenAPIV3.HttpMethods]?.parameters?.push(openapiParameter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ function createPlantUMLCodeForClass(config: UMLConfig, readerClass: LightweightC
|
||||
if (config.showProperties && readerClass.properties) {
|
||||
for (const key in readerClass.properties) {
|
||||
const property = readerClass.properties[key];
|
||||
if (property?.optional && !config.showOptionalProperties) {
|
||||
if (property.optional && !config.showOptionalProperties) {
|
||||
// don't show optional attributes
|
||||
continue;
|
||||
}
|
||||
@@ -202,7 +202,7 @@ function createPlantUMLCodeForClass(config: UMLConfig, readerClass: LightweightC
|
||||
// don't show inherited properties
|
||||
continue;
|
||||
}*/
|
||||
model += `\n\t${createPropertyLine(property!)}`;
|
||||
model += `\n\t${createPropertyLine(property)}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,13 +213,13 @@ function createPlantUMLCodeForClass(config: UMLConfig, readerClass: LightweightC
|
||||
if (readerClass.properties) {
|
||||
for (const key in readerClass.properties) {
|
||||
const property = readerClass.properties[key];
|
||||
const types: string[] = getReferenceTypes(property!.type);
|
||||
const types: string[] = getReferenceTypes(property.type);
|
||||
for (const type of types) {
|
||||
if (config.showAssociations) {
|
||||
/*if (property.inherited && !config.showInheritedProperties) {
|
||||
continue;
|
||||
}*/
|
||||
model += `${readerClass.name} -up-> ${type} : ${property!.name} >\n`;
|
||||
model += `${readerClass.name} -up-> ${type} : ${property.name} >\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ export class Validator {
|
||||
}
|
||||
|
||||
// schema will be cached
|
||||
return this.ajvValidateWrapper(this.schemas[schema]!, instance);
|
||||
return this.ajvValidateWrapper(this.schemas[schema], instance);
|
||||
}
|
||||
|
||||
return this.ajvValidateWrapper(schema, instance);
|
||||
@@ -147,7 +147,7 @@ function fromAjvResult(
|
||||
const error: ValidationError = {
|
||||
dataPath: ajvError.instancePath,
|
||||
instance: instance,
|
||||
message: betterErrorObject?.[index]?.error ?? ajvError.message!,
|
||||
message: betterErrorObject?.[index]?.error ?? ajvError.message,
|
||||
name: ajvError.keyword,
|
||||
schemaPath: ajvError.schemaPath,
|
||||
suggestion: betterErrorObject?.[index]?.suggestion,
|
||||
@@ -223,7 +223,7 @@ export async function validateFiles(
|
||||
}
|
||||
|
||||
// add error to list of errors
|
||||
errors[testFileName]?.push({
|
||||
errors[testFileName].push({
|
||||
...error,
|
||||
expected,
|
||||
});
|
||||
@@ -234,7 +234,7 @@ export async function validateFiles(
|
||||
for (const error of expectedErrors) {
|
||||
await Logger.error(`Extraneous expected error '${error}' in ${testFile}.`);
|
||||
|
||||
errors[testFileName]?.push({
|
||||
errors[testFileName].push({
|
||||
dataPath: 'undefined',
|
||||
expected: false,
|
||||
instance: undefined,
|
||||
@@ -279,7 +279,7 @@ export async function writeReport(reportPath: PathLike, errors: ExpectedValidati
|
||||
|
||||
let fileOutput = '';
|
||||
|
||||
for (const [index, error] of errors[fileName]!.entries()) {
|
||||
for (const [index, error] of errors[fileName].entries()) {
|
||||
fileOutput += mustache.render(errorTemplate, {
|
||||
idx: index + 1,
|
||||
instance: JSON.stringify(error.instance, undefined, 2),
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
# @openstapps/core
|
||||
|
||||
## 3.3.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 688bc5f2: v3.3.0 changes
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @openstapps/core-tools@3.3.0
|
||||
|
||||
## 3.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@openstapps/core",
|
||||
"description": "StAppsCore - Generalized model of data",
|
||||
"version": "3.3.0",
|
||||
"version": "3.2.0",
|
||||
"type": "module",
|
||||
"license": "GPL-3.0-only",
|
||||
"repository": "git@gitlab.com:openstapps/core.git",
|
||||
|
||||
@@ -102,14 +102,14 @@ export function isSearchResponse(something: unknown): something is SCSearchRespo
|
||||
const somethingObject = something as {[key: string]: {[key: string]: string}};
|
||||
|
||||
return (
|
||||
Array.isArray(somethingObject['data']) &&
|
||||
Array.isArray(somethingObject['facets']) &&
|
||||
somethingObject['pagination'] !== undefined &&
|
||||
typeof somethingObject['pagination']['count'] === 'number' &&
|
||||
typeof somethingObject['pagination']['offset'] === 'number' &&
|
||||
typeof somethingObject['pagination']['total'] === 'number' &&
|
||||
somethingObject['stats'] !== undefined &&
|
||||
typeof somethingObject['stats']['time'] === 'number'
|
||||
Array.isArray(somethingObject.data) &&
|
||||
Array.isArray(somethingObject.facets) &&
|
||||
somethingObject.pagination !== undefined &&
|
||||
typeof somethingObject.pagination.count === 'number' &&
|
||||
typeof somethingObject.pagination.offset === 'number' &&
|
||||
typeof somethingObject.pagination.total === 'number' &&
|
||||
somethingObject.stats !== undefined &&
|
||||
typeof somethingObject.stats.time === 'number'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ export class SCAcademicDegreeMeta extends SCThingMeta implements SCMetaTranslati
|
||||
/**
|
||||
* Translations of fields
|
||||
*/
|
||||
override fieldTranslations = {
|
||||
fieldTranslations = {
|
||||
de: {
|
||||
...new SCThingMeta().fieldTranslations.de,
|
||||
academicDegree: 'Abschlussgrad',
|
||||
@@ -73,7 +73,7 @@ export class SCAcademicDegreeMeta extends SCThingMeta implements SCMetaTranslati
|
||||
/**
|
||||
* Translations of values of fields
|
||||
*/
|
||||
override fieldValueTranslations = {
|
||||
fieldValueTranslations = {
|
||||
de: {
|
||||
...new SCThingMeta().fieldValueTranslations.de,
|
||||
},
|
||||
|
||||
@@ -67,7 +67,7 @@ export class SCAcademicTermWithoutReferencesMeta extends SCThingMeta implements
|
||||
/**
|
||||
* Translations of fields
|
||||
*/
|
||||
override fieldTranslations = {
|
||||
fieldTranslations = {
|
||||
de: {
|
||||
...new SCThingMeta().fieldTranslations.de,
|
||||
acronym: 'Akronym',
|
||||
@@ -89,7 +89,7 @@ export class SCAcademicTermWithoutReferencesMeta extends SCThingMeta implements
|
||||
/**
|
||||
* Translations of values of fields
|
||||
*/
|
||||
override fieldValueTranslations = {
|
||||
fieldValueTranslations = {
|
||||
de: {
|
||||
...new SCThingMeta().fieldValueTranslations.de,
|
||||
},
|
||||
|
||||
@@ -124,7 +124,7 @@ export class SCCreativeWorkMeta extends SCThingMeta implements SCMetaTranslation
|
||||
/**
|
||||
* Translations of fields
|
||||
*/
|
||||
override fieldTranslations = {
|
||||
fieldTranslations = {
|
||||
de: {
|
||||
...new SCThingMeta().fieldTranslations.de,
|
||||
name: 'Titel',
|
||||
@@ -162,7 +162,7 @@ export class SCCreativeWorkMeta extends SCThingMeta implements SCMetaTranslation
|
||||
/**
|
||||
* Translations of values of fields
|
||||
*/
|
||||
override fieldValueTranslations = {
|
||||
fieldValueTranslations = {
|
||||
de: {
|
||||
...new SCThingMeta().fieldValueTranslations.de,
|
||||
},
|
||||
|
||||
@@ -79,7 +79,7 @@ export class SCEventMeta extends SCThingMeta implements SCMetaTranslations<SCEve
|
||||
/**
|
||||
* Translations of fields
|
||||
*/
|
||||
override fieldTranslations = {
|
||||
fieldTranslations = {
|
||||
de: {
|
||||
...new SCThingMeta().fieldTranslations.de,
|
||||
academicTerms: 'Semester',
|
||||
@@ -105,7 +105,7 @@ export class SCEventMeta extends SCThingMeta implements SCMetaTranslations<SCEve
|
||||
/**
|
||||
* Translations of values of fields
|
||||
*/
|
||||
override fieldValueTranslations = {
|
||||
fieldValueTranslations = {
|
||||
de: {
|
||||
...new SCThingMeta().fieldValueTranslations.de,
|
||||
},
|
||||
|
||||
@@ -134,7 +134,7 @@ export class SCPlaceWithoutReferencesMeta
|
||||
/**
|
||||
* Translations of fields
|
||||
*/
|
||||
override fieldTranslations = {
|
||||
fieldTranslations = {
|
||||
de: {
|
||||
...new SCThingMeta().fieldTranslations.de,
|
||||
address: 'Adresse',
|
||||
@@ -152,7 +152,7 @@ export class SCPlaceWithoutReferencesMeta
|
||||
/**
|
||||
* Translations of values of fields
|
||||
*/
|
||||
override fieldValueTranslations = {
|
||||
fieldValueTranslations = {
|
||||
de: {
|
||||
...new SCThingMeta().fieldValueTranslations.de,
|
||||
},
|
||||
|
||||
@@ -30,7 +30,7 @@ export class SCThingInPlaceMeta extends SCThingMeta implements SCMetaTranslation
|
||||
/**
|
||||
* Translations of fields
|
||||
*/
|
||||
override fieldTranslations = {
|
||||
fieldTranslations = {
|
||||
de: {
|
||||
...new SCThingMeta().fieldTranslations.de,
|
||||
inPlace: 'Ort',
|
||||
@@ -44,7 +44,7 @@ export class SCThingInPlaceMeta extends SCThingMeta implements SCMetaTranslation
|
||||
/**
|
||||
* Translations of values of fields
|
||||
*/
|
||||
override fieldValueTranslations = {
|
||||
fieldValueTranslations = {
|
||||
de: {
|
||||
...new SCThingMeta().fieldValueTranslations.de,
|
||||
},
|
||||
|
||||
@@ -48,7 +48,7 @@ export class SCThingThatAcceptsPaymentsWithoutReferencesMeta
|
||||
/**
|
||||
* Translations of fields
|
||||
*/
|
||||
override fieldTranslations = {
|
||||
fieldTranslations = {
|
||||
de: {
|
||||
...new SCThingMeta().fieldTranslations.de,
|
||||
paymentsAccepted: 'Bezahlmethoden',
|
||||
@@ -62,7 +62,7 @@ export class SCThingThatAcceptsPaymentsWithoutReferencesMeta
|
||||
/**
|
||||
* Translations of values of fields
|
||||
*/
|
||||
override fieldValueTranslations = {
|
||||
fieldValueTranslations = {
|
||||
de: {
|
||||
...new SCThingMeta().fieldValueTranslations.de,
|
||||
paymentsAccepted: {
|
||||
|
||||
@@ -119,7 +119,7 @@ export class SCAcademicEventMeta extends SCThingMeta implements SCMetaTranslatio
|
||||
/**
|
||||
* Translations of fields
|
||||
*/
|
||||
override fieldTranslations = {
|
||||
fieldTranslations = {
|
||||
de: {
|
||||
...new SCEventMeta().fieldTranslations.de,
|
||||
...new SCThingWithCategoriesWithoutReferencesMeta<
|
||||
@@ -143,7 +143,7 @@ export class SCAcademicEventMeta extends SCThingMeta implements SCMetaTranslatio
|
||||
/**
|
||||
* Translations of values of fields
|
||||
*/
|
||||
override fieldValueTranslations = {
|
||||
fieldValueTranslations = {
|
||||
de: {
|
||||
...new SCEventMeta().fieldValueTranslations.de,
|
||||
...new SCThingWithCategoriesWithoutReferencesMeta<
|
||||
|
||||
@@ -119,7 +119,7 @@ export class SCArticleMeta extends SCThingMeta implements SCMetaTranslations<SCA
|
||||
/**
|
||||
* Translations of fields
|
||||
*/
|
||||
override fieldTranslations = {
|
||||
fieldTranslations = {
|
||||
de: {
|
||||
...new SCCreativeWorkMeta().fieldTranslations.de,
|
||||
...new SCThingWithCategoriesWithoutReferencesMeta<
|
||||
@@ -147,7 +147,7 @@ export class SCArticleMeta extends SCThingMeta implements SCMetaTranslations<SCA
|
||||
/**
|
||||
* Translations of values of fields
|
||||
*/
|
||||
override fieldValueTranslations = {
|
||||
fieldValueTranslations = {
|
||||
de: {
|
||||
...new SCCreativeWorkMeta().fieldValueTranslations.de,
|
||||
...new SCThingThatCanBeOfferedMeta<SCAcademicPriceGroup>().fieldValueTranslations.de,
|
||||
|
||||
@@ -120,7 +120,7 @@ export class SCAssessmentMeta extends SCThingMeta implements SCMetaTranslations<
|
||||
/**
|
||||
* Translations of fields
|
||||
*/
|
||||
override fieldTranslations = {
|
||||
fieldTranslations = {
|
||||
de: {
|
||||
...new SCThingWithCategoriesWithoutReferencesMeta<
|
||||
SCAssessmentCategories,
|
||||
@@ -152,7 +152,7 @@ export class SCAssessmentMeta extends SCThingMeta implements SCMetaTranslations<
|
||||
/**
|
||||
* Translations of values of fields
|
||||
*/
|
||||
override fieldValueTranslations = {
|
||||
fieldValueTranslations = {
|
||||
de: {
|
||||
...new SCThingWithCategoriesWithoutReferencesMeta<
|
||||
SCAssessmentCategories,
|
||||
|
||||
@@ -131,7 +131,7 @@ export class SCBookMeta extends SCThingMeta implements SCMetaTranslations<SCBook
|
||||
/**
|
||||
* Translations of fields
|
||||
*/
|
||||
override fieldTranslations = {
|
||||
fieldTranslations = {
|
||||
de: {
|
||||
...new SCCreativeWorkMeta().fieldTranslations.de,
|
||||
...new SCThingWithCategoriesWithoutReferencesMeta<
|
||||
@@ -159,7 +159,7 @@ export class SCBookMeta extends SCThingMeta implements SCMetaTranslations<SCBook
|
||||
/**
|
||||
* Translations of values of fields
|
||||
*/
|
||||
override fieldValueTranslations = {
|
||||
fieldValueTranslations = {
|
||||
de: {
|
||||
...new SCCreativeWorkMeta().fieldValueTranslations.de,
|
||||
...new SCThingWithCategoriesWithoutReferencesMeta<
|
||||
|
||||
@@ -95,7 +95,7 @@ export class SCBuildingMeta extends SCThingMeta implements SCMetaTranslations<SC
|
||||
/**
|
||||
* Translations of fields
|
||||
*/
|
||||
override fieldTranslations = {
|
||||
fieldTranslations = {
|
||||
de: {
|
||||
...new SCThingWithCategoriesWithoutReferencesMeta<
|
||||
SCBuildingCategories,
|
||||
@@ -117,7 +117,7 @@ export class SCBuildingMeta extends SCThingMeta implements SCMetaTranslations<SC
|
||||
/**
|
||||
* Translations of values of fields
|
||||
*/
|
||||
override fieldValueTranslations = {
|
||||
fieldValueTranslations = {
|
||||
de: {
|
||||
...new SCThingWithCategoriesWithoutReferencesMeta<
|
||||
SCBuildingCategories,
|
||||
|
||||
@@ -85,7 +85,7 @@ export class SCCatalogMeta extends SCThingMeta implements SCMetaTranslations<SCC
|
||||
/**
|
||||
* Translations of fields
|
||||
*/
|
||||
override fieldTranslations = {
|
||||
fieldTranslations = {
|
||||
de: {
|
||||
...new SCThingWithCategoriesWithoutReferencesMeta<
|
||||
SCCatalogCategories,
|
||||
@@ -111,7 +111,7 @@ export class SCCatalogMeta extends SCThingMeta implements SCMetaTranslations<SCC
|
||||
/**
|
||||
* Translations of values of fields
|
||||
*/
|
||||
override fieldValueTranslations = {
|
||||
fieldValueTranslations = {
|
||||
de: {
|
||||
...new SCThingWithCategoriesWithoutReferencesMeta<
|
||||
SCCatalogCategories,
|
||||
|
||||
@@ -80,7 +80,7 @@ export type SCCertificationCategories =
|
||||
| 'rainforest protection';
|
||||
|
||||
export class SCCertificationMeta extends SCThingMeta implements SCMetaTranslations<SCCertification> {
|
||||
override fieldTranslations = {
|
||||
fieldTranslations = {
|
||||
de: {
|
||||
...new SCThingMeta().fieldTranslations.de,
|
||||
...new SCThingWithCategoriesWithoutReferencesMeta<
|
||||
@@ -101,7 +101,7 @@ export class SCCertificationMeta extends SCThingMeta implements SCMetaTranslatio
|
||||
},
|
||||
};
|
||||
|
||||
override fieldValueTranslations = {
|
||||
fieldValueTranslations = {
|
||||
de: {
|
||||
...new SCThingMeta().fieldValueTranslations.de,
|
||||
...new SCThingWithCategoriesWithoutReferencesMeta<
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user