Merge remote-tracking branch 'app/develop'

This commit is contained in:
2023-05-24 14:31:00 +02:00
152 changed files with 2765 additions and 1866 deletions

View File

@@ -1 +1,2 @@
src/app/_helpers/data
node_modules

View File

@@ -8,7 +8,7 @@
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module",
"project": ["tsconfig.json", "e2e/tsconfig.e2e.json"],
"project": ["tsconfig.json", "tsconfig.spec.json", "e2e/tsconfig.e2e.json"],
"createDefaultProgram": true
},
"extends": [
@@ -44,7 +44,6 @@
],
"unicorn/no-nested-ternary": "off",
"unicorn/better-regex": "off",
"jsdoc/no-types": "error",
"jsdoc/require-param": "off",
"jsdoc/require-param-description": "error",
@@ -52,7 +51,6 @@
"jsdoc/require-returns": "off",
"jsdoc/require-param-type": "off",
"jsdoc/require-returns-type": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unused-vars": [
"error",

View File

@@ -26,7 +26,7 @@ setup:
services:
- docker:dind
script:
- docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.gitlab.com
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.gitlab.com
- docker build -t registry.gitlab.com/openstapps/app .
- docker push registry.gitlab.com/openstapps/app
cache: {} # disable irrelevant cache for this job

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 StApps
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -22,7 +22,7 @@ describe('App', () => {
it('should have a proper working navigation', () => {
cy.visit('/');
cy.contains('Einstellungen').click();
cy.get('ion-title').contains('Einstellungen');
cy.contains('ion-tab-button', 'Mensa').click();
cy.get('ion-title').contains('Mensa');
});
});

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 StApps
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -54,14 +54,14 @@ describe('context menu', function () {
it('should truncate categories', function () {
cy.get('stapps-context').within(() => {
cy.contains('ion-item', '(4) Universitätsveranstaltung').should('not.exist');
cy.contains('ion-item', '(1) Universitätsveranstaltung').should('not.exist');
cy.get('.context-filter > ion-button').click();
cy.contains('ion-item', '(4) Universitätsveranstaltung').should('exist');
});
});
it('should truncate long category items', function () {
cy.contains('ion-list', 'Kategorien | Akademische Veranstaltung').within(() => {
cy.contains('ion-list', 'Akademische Veranstaltung / Kategorien').within(() => {
cy.contains('ion-item', '(1) Tutorium').should('not.exist');
cy.get('div > ion-button').click();
cy.contains('ion-item', '(1) Tutorium').should('exist');

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 StApps
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -40,7 +40,6 @@ describe('favorites', function () {
cy.get('.title').should('contain', text);
cy.get('stapps-favorite-button').click();
});
cy.get('cdk-virtual-scroll-viewport').should('be.not.visible');
cy.get('stapps-data-list').contains('Keine Ergebnisse').should('be.visible');
});
});

View File

@@ -30,14 +30,14 @@ describe('ical', function () {
cy.get('ion-app > ion-modal').within(() => {
cy.get('ion-footer > ion-toolbar > ion-button').should('have.attr', 'disabled');
cy.contains('ion-item', /eine Stunde um 19. Jan. 2059, \d+:00/).click();
cy.contains('ion-item', /19\.\s+Januar\s+2059,\s+\d{2}:00\s+-\s+\d{2}:00/).click();
cy.get('ion-footer > ion-toolbar > ion-button').should('not.have.attr', 'disabled');
cy.get('ion-footer > ion-toolbar > ion-button').click();
});
cy.get('add-event-review-modal').within(() => {
cy.get('ion-item-group').should('contain', 'UNIcert (Test)');
cy.contains('ion-item-group', /19. Jan. 2059, \d+:00/);
cy.contains('ion-item-group', /19\.\s+Jan\.\s+2059,\s+\d{2}:00/);
});
});
});

View File

@@ -4,5 +4,6 @@
"compilerOptions": {
"sourceMap": false,
"types": ["cypress"]
}
},
"exclude": []
}

View File

@@ -1,6 +1,6 @@
{
"name": "@openstapps/app",
"version": "2.0.0",
"version": "2.1.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -2735,6 +2735,7 @@
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz",
"integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==",
"dev": true,
"requires": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
@@ -2751,6 +2752,7 @@
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -2761,7 +2763,8 @@
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
}
}
},
@@ -2844,25 +2847,19 @@
"integrity": "sha512-hebf0ixGPugiZfH6g7HS/hrDzkKmNdJV/pV2jUz5lfoZXFMjE+7aeAr1AqwW6EGNej65WcEP8VUL5YUc3wSCjw=="
},
"@humanwhocodes/config-array": {
"version": "0.10.7",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz",
"integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==",
"version": "0.11.8",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
"integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
"requires": {
"@humanwhocodes/object-schema": "^1.2.1",
"debug": "^4.1.1",
"minimatch": "^3.0.4"
"minimatch": "^3.0.5"
}
},
"@humanwhocodes/gitignore-to-minimatch": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz",
"integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA=="
},
"@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
"dev": true
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="
},
"@humanwhocodes/momoa": {
"version": "2.0.4",
@@ -4022,26 +4019,26 @@
}
},
"@openstapps/api": {
"version": "0.45.0",
"resolved": "https://registry.npmjs.org/@openstapps/api/-/api-0.45.0.tgz",
"integrity": "sha512-lF1TIxbtqQlRYCvSyS3EDjQiwVK7BmZ3/HQ01MhlUR1ucfvMTk1GyIPx5E8HmrKSi3Pv8shurCObSwStQJjK+Q==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@openstapps/api/-/api-1.0.1.tgz",
"integrity": "sha512-M7aBEikw730t+jYIDYrJrdNhNvA0N1y9j6qGz2XAKwXbZN7NwDgs/41BCsPLPJgPPRGDL1bH/PbcbkKCn/vTTg==",
"requires": {
"@krlwlfrt/async-pool": "0.7.0",
"@openstapps/core": "0.72.0",
"@openstapps/core-tools": "0.32.0",
"@openstapps/core": "1.0.1",
"@openstapps/core-tools": "0.34.0",
"@openstapps/logger": "1.1.1",
"@types/cli-progress": "3.11.0",
"@types/express": "4.17.14",
"@types/morgan": "1.9.3",
"@types/node": "14.18.34",
"@types/express": "4.17.17",
"@types/morgan": "1.9.4",
"@types/node": "14.18.43",
"@types/traverse": "0.6.32",
"@types/uuid": "8.3.4",
"@types/wait-on": "5.3.1",
"body-parser": "1.20.1",
"cli-progress": "3.11.2",
"commander": "9.4.1",
"body-parser": "1.20.2",
"cli-progress": "3.12.0",
"commander": "9.5.0",
"express": "4.18.2",
"got": "11.8.5",
"got": "11.8.6",
"json-schema": "0.4.0",
"moment": "2.29.4",
"morgan": "1.10.0",
@@ -4051,164 +4048,200 @@
"wait-on": "6.0.1"
},
"dependencies": {
"@types/express": {
"version": "4.17.17",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
"integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==",
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.33",
"@types/qs": "*",
"@types/serve-static": "*"
}
},
"@types/express-serve-static-core": {
"version": "4.17.34",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.34.tgz",
"integrity": "sha512-fvr49XlCGoUj2Pp730AItckfjat4WNb0lb3kfrLWffd+RLeoGAMsq7UOy04PAPtoL01uKwcp6u8nhzpgpDYr3w==",
"requires": {
"@types/node": "*",
"@types/qs": "*",
"@types/range-parser": "*",
"@types/send": "*"
}
},
"@types/node": {
"version": "14.18.34",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.34.tgz",
"integrity": "sha512-hcU9AIQVHmPnmjRK+XUUYlILlr9pQrsqSrwov/JK1pnf3GTQowVBhx54FbvM0AU/VXGH4i3+vgXS5EguR7fysA=="
"version": "14.18.43",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.43.tgz",
"integrity": "sha512-n3eFEaoem0WNwLux+k272P0+aq++5o05bA9CfiwKPdYPB5ZambWKdWoeHy7/OJiizMhzg27NLaZ6uzjLTzXceQ=="
},
"body-parser": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"requires": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
}
},
"commander": {
"version": "9.5.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="
},
"content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"raw-body": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"requires": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
}
}
},
"@openstapps/configuration": {
"version": "0.33.0",
"resolved": "https://registry.npmjs.org/@openstapps/configuration/-/configuration-0.33.0.tgz",
"integrity": "sha512-sum9DB8+2r5eXnJhie1UPcQQTPupd0m3Xgsft+D6LVvbjbmt/XCpCMvlZSoM+LE1ZkUdgUmbFJ81mqjWKgCsJA==",
"version": "0.34.0",
"resolved": "https://registry.npmjs.org/@openstapps/configuration/-/configuration-0.34.0.tgz",
"integrity": "sha512-woPn4v3mQMUibtRs84VDpiC0EarowA4HvtpXbzc6ddWcYs8jtPLwA+mSbWSiROhJ2CeRQTJtMK+bJ4yhIRfdKw==",
"requires": {
"@types/node": "14.18.24",
"@types/semver": "7.3.12",
"@types/node": "14.18.36",
"@types/semver": "7.3.13",
"@types/yaml": "1.9.7",
"chalk": "4.1.2",
"commander": "9.4.0",
"semver": "7.3.7",
"commander": "9.5.0",
"semver": "7.3.8",
"yaml": "1.10.2"
},
"dependencies": {
"@types/node": {
"version": "14.18.36",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.36.tgz",
"integrity": "sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ=="
},
"@types/semver": {
"version": "7.3.13",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
"integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw=="
},
"commander": {
"version": "9.4.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz",
"integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw=="
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"requires": {
"yallist": "^4.0.0"
}
},
"semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"requires": {
"lru-cache": "^6.0.0"
}
"version": "9.5.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="
}
}
},
"@openstapps/core": {
"version": "0.72.0",
"resolved": "https://registry.npmjs.org/@openstapps/core/-/core-0.72.0.tgz",
"integrity": "sha512-bT22CWKf0Do32FwJLf+rWxbQTHTiPbJEzm3dd3Sk5utGgNTAeFequaNea2csvCOA1dRaVrRVQ7Ed9prVdep9ow==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@openstapps/core/-/core-1.0.1.tgz",
"integrity": "sha512-+JlycoMcj+QIaXicOZYlNU07XlDc3zRIydYEoLGueAfkTbKt0ap5FWbNL+Hz8ve3ApQP2Hj+4FuU8H6QyLA0vQ==",
"requires": {
"@openstapps/core-tools": "0.32.0",
"@openstapps/core-tools": "0.34.0",
"@types/geojson": "1.0.6",
"@types/json-patch": "0.0.30",
"@types/json-schema": "7.0.11",
"@types/node": "14.18.24",
"@types/node": "14.18.36",
"fast-deep-equal": "3.1.3",
"http-status-codes": "2.2.0",
"json-patch": "0.7.0",
"json-schema": "0.4.0",
"rfdc": "1.3.0",
"ts-optchain": "0.1.8"
},
"dependencies": {
"@types/node": {
"version": "14.18.36",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.36.tgz",
"integrity": "sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ=="
}
}
},
"@openstapps/core-tools": {
"version": "0.32.0",
"resolved": "https://registry.npmjs.org/@openstapps/core-tools/-/core-tools-0.32.0.tgz",
"integrity": "sha512-PXfb9b3B2uVQUhu+QBi3tlz7I2nHz4hlyTNvfHIxuO8pC0ONaZWY56tjBgqg1Af9BP1ZGq3J3Zg2sWWsYlGFEw==",
"version": "0.34.0",
"resolved": "https://registry.npmjs.org/@openstapps/core-tools/-/core-tools-0.34.0.tgz",
"integrity": "sha512-gCnmBX0Mp1vf9p66i64u2lQAXBTKxI6Lt92ZUxkrMcIv0CmGAEw1mEEgg4hf4nRDm0umBSu0tRfh7DFnuQyOEA==",
"requires": {
"@openstapps/logger": "1.0.0",
"ajv": "8.11.0",
"@openstapps/logger": "1.1.1",
"ajv": "8.12.0",
"better-ajv-errors": "1.2.0",
"chai": "4.3.6",
"commander": "9.4.0",
"deepmerge": "4.2.2",
"chai": "4.3.7",
"commander": "10.0.0",
"deepmerge": "4.3.0",
"del": "6.1.1",
"eslint": "8.22.0",
"flatted": "3.2.6",
"eslint": "8.33.0",
"flatted": "3.2.7",
"fs-extra": "10.1.0",
"glob": "8.0.3",
"got": "11.8.5",
"glob": "8.1.0",
"got": "11.8.6",
"humanize-string": "3.0.0",
"json-schema": "0.4.0",
"lodash": "4.17.21",
"mustache": "4.2.0",
"openapi-types": "12.0.0",
"openapi-types": "12.1.0",
"plantuml-encoder": "1.4.0",
"re2": "1.17.7",
"re2": "1.18.0",
"toposort": "2.0.2",
"ts-json-schema-generator": "1.0.0",
"ts-json-schema-generator": "1.2.0",
"ts-node": "10.9.1"
},
"dependencies": {
"@openstapps/logger": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@openstapps/logger/-/logger-1.0.0.tgz",
"integrity": "sha512-ImCfnLJWHWwFJ1W7KCIWgFe7EXI0cAtap0Dznz+758BPAlLZ+hJHSbIbGHWwEAfRNAB53TIABF5npSsKSDq4Sw==",
"@eslint/eslintrc": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz",
"integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==",
"requires": {
"@types/node": "14.18.24",
"@types/nodemailer": "6.4.5",
"chalk": "4.1.2",
"flatted": "3.2.6",
"moment": "2.29.4",
"nodemailer": "6.7.8"
}
},
"commander": {
"version": "9.4.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz",
"integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw=="
},
"escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
},
"eslint": {
"version": "8.22.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.22.0.tgz",
"integrity": "sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==",
"requires": {
"@eslint/eslintrc": "^1.3.0",
"@humanwhocodes/config-array": "^0.10.4",
"@humanwhocodes/gitignore-to-minimatch": "^1.0.2",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"ajv": "^6.12.4",
"debug": "^4.3.2",
"doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^7.1.1",
"eslint-utils": "^3.0.0",
"eslint-visitor-keys": "^3.3.0",
"espree": "^9.3.3",
"esquery": "^1.4.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
"file-entry-cache": "^6.0.1",
"find-up": "^5.0.0",
"functional-red-black-tree": "^1.0.1",
"glob-parent": "^6.0.1",
"globals": "^13.15.0",
"globby": "^11.1.0",
"grapheme-splitter": "^1.0.4",
"espree": "^9.4.0",
"globals": "^13.19.0",
"ignore": "^5.2.0",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
"optionator": "^0.9.1",
"regexpp": "^3.2.0",
"strip-ansi": "^6.0.1",
"strip-json-comments": "^3.1.0",
"text-table": "^0.2.0",
"v8-compile-cache": "^2.0.3"
"strip-json-comments": "^3.1.1"
},
"dependencies": {
"ajv": {
@@ -4221,6 +4254,136 @@
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
}
}
},
"ajv": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
}
},
"brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"requires": {
"balanced-match": "^1.0.0"
}
},
"commander": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.0.tgz",
"integrity": "sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA=="
},
"deepmerge": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz",
"integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og=="
},
"escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
},
"eslint": {
"version": "8.33.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz",
"integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==",
"requires": {
"@eslint/eslintrc": "^1.4.1",
"@humanwhocodes/config-array": "^0.11.8",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
"doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^7.1.1",
"eslint-utils": "^3.0.0",
"eslint-visitor-keys": "^3.3.0",
"espree": "^9.4.0",
"esquery": "^1.4.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
"file-entry-cache": "^6.0.1",
"find-up": "^5.0.0",
"glob-parent": "^6.0.2",
"globals": "^13.19.0",
"grapheme-splitter": "^1.0.4",
"ignore": "^5.2.0",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"is-path-inside": "^3.0.3",
"js-sdsl": "^4.1.4",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
"optionator": "^0.9.1",
"regexpp": "^3.2.0",
"strip-ansi": "^6.0.1",
"strip-json-comments": "^3.1.0",
"text-table": "^0.2.0"
},
"dependencies": {
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
}
}
},
"flatted": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ=="
},
"glob": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^5.0.1",
"once": "^1.3.0"
},
"dependencies": {
"minimatch": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"requires": {
"brace-expansion": "^2.0.1"
}
}
}
},
@@ -4232,10 +4395,13 @@
"is-glob": "^4.0.3"
}
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
"globals": {
"version": "13.20.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
"integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
"requires": {
"type-fest": "^0.20.2"
}
}
}
},
@@ -4257,23 +4423,10 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.32.tgz",
"integrity": "sha512-Y6S38pFr04yb13qqHf8uk1nHE3lXgQ30WZbv1mLliV9pt0NjvqdWttLcrOYLnXbOafknVYRHZGoMSpR9UwfYow=="
},
"@types/nodemailer": {
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.6.tgz",
"integrity": "sha512-pD6fL5GQtUKvD2WnPmg5bC2e8kWCAPDwMPmHe/ohQbW+Dy0EcHgZ2oCSuPlWNqk74LS5BVMig1SymQbFMPPK3w==",
"requires": {
"@types/node": "*"
}
},
"flatted": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ=="
},
"nodemailer": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.8.0.tgz",
"integrity": "sha512-EjYvSmHzekz6VNkNd12aUqAco+bOkRe3Of5jVhltqKhEsjw/y0PYPJfp83+s9Wzh1dspYAkUW/YNQ350NATbSQ=="
}
}
},
@@ -4327,9 +4480,9 @@
}
},
"@sideway/formula": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz",
"integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg=="
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz",
"integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg=="
},
"@sideway/pinpoint": {
"version": "2.0.0",
@@ -4521,6 +4674,7 @@
"version": "4.17.14",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz",
"integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==",
"dev": true,
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.18",
@@ -4532,6 +4686,7 @@
"version": "4.17.31",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz",
"integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==",
"dev": true,
"requires": {
"@types/node": "*",
"@types/qs": "*",
@@ -4679,9 +4834,9 @@
"dev": true
},
"@types/morgan": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.3.tgz",
"integrity": "sha512-BiLcfVqGBZCyNCnCH3F4o2GmDLrpy0HeBVnNlyZG4fo88ZiE9SoiBe3C+2ezuwbjlEyT+PDZ17//TAlRxAn75Q==",
"version": "1.9.4",
"resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.4.tgz",
"integrity": "sha512-cXoc4k+6+YAllH3ZHmx4hf7La1dzUk6keTR4bF4b4Sc0mZxU/zK4wO7l+ZzezXm/jkYj/qC+uYGZrarZdIVvyQ==",
"requires": {
"@types/node": "*"
}
@@ -4692,9 +4847,9 @@
"integrity": "sha512-aJdn8XErcSrfr7k8ZDDfU6/2OgjZcB2Fu9d+ESK8D7Oa5mtsv8Fa8GpcwTA0v60kuZBaalKPzuzun4Ov1YWO/w=="
},
"@types/nodemailer": {
"version": "6.4.5",
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.5.tgz",
"integrity": "sha512-zuP3nBRQHI6M2PkXnGGy1Ww4VB+MyYHGgnfV2T+JR9KLkeWqPJuyVUgLpKXuFnA/b7pZaIDFh2sV4759B7jK1g==",
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.6.tgz",
"integrity": "sha512-pD6fL5GQtUKvD2WnPmg5bC2e8kWCAPDwMPmHe/ohQbW+Dy0EcHgZ2oCSuPlWNqk74LS5BVMig1SymQbFMPPK3w==",
"requires": {
"@types/node": "*"
}
@@ -4750,7 +4905,24 @@
"@types/semver": {
"version": "7.3.12",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.12.tgz",
"integrity": "sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A=="
"integrity": "sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A==",
"dev": true
},
"@types/send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz",
"integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==",
"requires": {
"@types/mime": "^1",
"@types/node": "*"
},
"dependencies": {
"@types/mime": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
}
}
},
"@types/serve-index": {
"version": "1.9.1",
@@ -5494,6 +5666,7 @@
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@@ -6448,13 +6621,13 @@
"dev": true
},
"chai": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz",
"integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==",
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz",
"integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==",
"requires": {
"assertion-error": "^1.1.0",
"check-error": "^1.0.2",
"deep-eql": "^3.0.1",
"deep-eql": "^4.1.2",
"get-func-name": "^2.0.0",
"loupe": "^2.3.1",
"pathval": "^1.1.1",
@@ -6689,9 +6862,9 @@
}
},
"cli-progress": {
"version": "3.11.2",
"resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.11.2.tgz",
"integrity": "sha512-lCPoS6ncgX4+rJu5bS3F/iCz17kZ9MPZ6dpuTtI0KXKABkhyXIdYB3Inby1OpaGti3YlI3EeEkM9AuWpelJrVA==",
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz",
"integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==",
"requires": {
"string-width": "^4.2.3"
}
@@ -7827,9 +8000,9 @@
}
},
"deep-eql": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
"integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
"integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==",
"requires": {
"type-detect": "^4.0.0"
}
@@ -9653,11 +9826,6 @@
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"functional-red-black-tree": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
"integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g=="
},
"functions-have-names": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
@@ -10020,6 +10188,7 @@
"version": "13.18.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz",
"integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==",
"dev": true,
"requires": {
"type-fest": "^0.20.2"
}
@@ -10038,9 +10207,9 @@
}
},
"got": {
"version": "11.8.5",
"resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz",
"integrity": "sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==",
"version": "11.8.6",
"resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
"integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
"requires": {
"@sindresorhus/is": "^4.0.0",
"@szmarczak/http-timer": "^4.0.5",
@@ -10646,9 +10815,9 @@
"dev": true
},
"install-artifact-from-github": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/install-artifact-from-github/-/install-artifact-from-github-1.3.1.tgz",
"integrity": "sha512-3l3Bymg2eKDsN5wQuMfgGEj2x6l5MCAv0zPL6rxHESufFVlEAKW/6oY9F1aGgvY/EgWm5+eWGRjINveL4X7Hgg=="
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/install-artifact-from-github/-/install-artifact-from-github-1.3.2.tgz",
"integrity": "sha512-yCFcLvqk0yQdxx0uJz4t9Z3adDMLAYrcGYv546uRXCSvxE+GqNYhhz/KmrGcUKGI/gVLR9n/e/zM9jX/+ASMJQ=="
},
"ionic-appauth": {
"version": "0.9.0",
@@ -11154,22 +11323,21 @@
"dev": true
},
"joi": {
"version": "17.7.0",
"resolved": "https://registry.npmjs.org/joi/-/joi-17.7.0.tgz",
"integrity": "sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==",
"version": "17.9.2",
"resolved": "https://registry.npmjs.org/joi/-/joi-17.9.2.tgz",
"integrity": "sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw==",
"requires": {
"@hapi/hoek": "^9.0.0",
"@hapi/topo": "^5.0.0",
"@sideway/address": "^4.1.3",
"@sideway/formula": "^3.0.0",
"@sideway/formula": "^3.0.1",
"@sideway/pinpoint": "^2.0.0"
}
},
"js-sdsl": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz",
"integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==",
"dev": true
"integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ=="
},
"js-tokens": {
"version": "4.0.0",
@@ -12896,9 +13064,9 @@
"dev": true
},
"node-gyp": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.3.0.tgz",
"integrity": "sha512-A6rJWfXFz7TQNjpldJ915WFb1LnhO4lIve3ANPbWreuEoLoKlFT3sxIepPBkLhM27crW8YmN+pjlgbasH6cH/Q==",
"version": "9.3.1",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.3.1.tgz",
"integrity": "sha512-4Q16ZCqq3g8awk6UplT7AuxQ35XN4R/yf/+wSAwcBUAjg7l58RTactWaP8fIDTi0FzI7YcVLujwExakZlfWkXg==",
"requires": {
"env-paths": "^2.2.0",
"glob": "^7.1.4",
@@ -12940,9 +13108,9 @@
"dev": true
},
"nodemailer": {
"version": "6.7.8",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.8.tgz",
"integrity": "sha512-2zaTFGqZixVmTxpJRCFC+Vk5eGRd/fYtvIR+dl5u9QXLTQWGIf48x/JXvo58g9sa0bU6To04XUv554Paykum3g=="
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.8.0.tgz",
"integrity": "sha512-EjYvSmHzekz6VNkNd12aUqAco+bOkRe3Of5jVhltqKhEsjw/y0PYPJfp83+s9Wzh1dspYAkUW/YNQ350NATbSQ=="
},
"nopt": {
"version": "6.0.0",
@@ -12967,8 +13135,7 @@
"normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
},
"normalize-range": {
"version": "0.1.2",
@@ -13203,9 +13370,9 @@
}
},
"openapi-types": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.0.0.tgz",
"integrity": "sha512-6Wd9k8nmGQHgCbehZCP6wwWcfXcvinhybUTBatuhjRsCxUIujuYFZc9QnGeae75CyHASewBtxs0HX/qwREReUw=="
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.0.tgz",
"integrity": "sha512-XpeCy01X6L5EpP+6Hc3jWN7rMZJ+/k1lwki/kTmWzbVhdPie3jd5O2ZtedEx8Yp58icJ0osVldLMrTB/zslQXA=="
},
"opencollective-postinstall": {
"version": "2.0.3",
@@ -14907,13 +15074,13 @@
}
},
"re2": {
"version": "1.17.7",
"resolved": "https://registry.npmjs.org/re2/-/re2-1.17.7.tgz",
"integrity": "sha512-X8GSuiBoVWwcjuppqSjsIkRxNUKDdjhkO9SBekQbZ2ksqWUReCy7DQPWOVpoTnpdtdz5PIpTTxTFzvJv5UMfjA==",
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/re2/-/re2-1.18.0.tgz",
"integrity": "sha512-MoCYZlJ9YUgksND9asyNF2/x532daXU/ARp1UeJbQ5flMY6ryKNEhrWt85aw3YluzOJlC3vXpGgK2a1jb0b4GA==",
"requires": {
"install-artifact-from-github": "^1.3.1",
"nan": "^2.16.0",
"node-gyp": "^9.0.0"
"nan": "^2.17.0",
"node-gyp": "^9.3.0"
}
},
"read": {
@@ -15553,9 +15720,9 @@
}
},
"safe-stable-stringify": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.1.tgz",
"integrity": "sha512-dVHE6bMtS/bnL2mwualjc6IxEv1F+OCUpA46pKUj6F8uDbUM0jCCulPqRNPSnWwGNKx5etqMjZYdXtrm5KJZGA=="
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz",
"integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g=="
},
"safer-buffer": {
"version": "2.1.2",
@@ -17242,35 +17409,23 @@
"dev": true
},
"ts-json-schema-generator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-1.0.0.tgz",
"integrity": "sha512-F5VofsyMhNSXKII32NDS8/Ur8o2K3Sh5i/U2ke3UgCKf26ybgm2cZeT2x7VJPl1trML/9QLzz/82l0mvzmb3Vw==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-1.2.0.tgz",
"integrity": "sha512-tUMeO3ZvA12d3HHh7T/AK8W5hmUhDRNtqWRHSMN3ZRbUFt+UmV0oX8k1RK4SA+a+BKNHpmW2v06MS49e8Fi3Yg==",
"requires": {
"@types/json-schema": "^7.0.9",
"commander": "^9.0.0",
"glob": "^7.2.0",
"json5": "^2.2.0",
"safe-stable-stringify": "^2.3.1",
"typescript": "~4.6.2"
"@types/json-schema": "^7.0.11",
"commander": "^9.4.1",
"glob": "^8.0.3",
"json5": "^2.2.1",
"normalize-path": "^3.0.0",
"safe-stable-stringify": "^2.4.1",
"typescript": "~4.9.3"
},
"dependencies": {
"glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"typescript": {
"version": "4.6.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz",
"integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg=="
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g=="
}
}
},
@@ -17584,7 +17739,8 @@
"v8-compile-cache": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
"integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA=="
"integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
"dev": true
},
"v8-compile-cache-lib": {
"version": "3.0.1",
@@ -17665,16 +17821,6 @@
"lodash": "^4.17.21",
"minimist": "^1.2.5",
"rxjs": "^7.5.4"
},
"dependencies": {
"rxjs": {
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.6.0.tgz",
"integrity": "sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ==",
"requires": {
"tslib": "^2.1.0"
}
}
}
},
"watchpack": {

View File

@@ -1,6 +1,6 @@
{
"name": "@openstapps/app",
"version": "2.0.0",
"version": "2.1.1",
"description": "The generic app tailored to fulfill needs of German universities, written using Ionic Framework.",
"license": "GPL-3.0-only",
"author": "Karl-Philipp Wulfert <krlwlfrt@gmail.com>",
@@ -19,7 +19,7 @@
"build:android": "ionic capacitor build android --no-open && cd android && ./gradlew clean assembleDebug && cd ..",
"build:prod": "ng build --configuration=production",
"build:stats": "ng build --configuration=production --stats-json",
"changelog": "conventional-changelog -p angular -i src/assets/about/CHANGELOG.md -s -r 0 && git add src/assets/about/CHANGELOG.md",
"changelog": "conventional-changelog -p angular -i src/assets/about/CHANGELOG.md -s -r 0",
"check-configuration": "openstapps-configuration",
"cypress:open": "cypress open",
"cypress:run": "cypress run",
@@ -31,7 +31,7 @@
"docker:serve": "sudo docker run -p 8100:8100 -p 35729:35729 -p 53703:53703 -v $PWD:/app -it registry.gitlab.com/openstapps/app bash -c \"npm run start:external\"",
"documentation": "compodoc -p tsconfig.json -d docs",
"e2e": "ng e2e",
"licenses": "license-checker --json > src/assets/about/licenses.json && ts-node ./scripts/accumulate-licenses.ts && git add src/assets/about/licenses.json",
"licenses": "license-checker --json > src/assets/about/licenses.json && ts-node ./scripts/accumulate-licenses.ts",
"minify-icons": "ts-node scripts/minify-icon-font.ts",
"check-icons": "ts-node scripts/check-icon-correctness.ts",
"format:check": "prettier --check .",
@@ -40,7 +40,7 @@
"lint:fix": "eslint --fix -c .eslintrc.json --ignore-path .eslintignore --ext .ts,.html src/",
"ng": "ng",
"postinstall": "npx jetify",
"version": "npm run changelog && npm run licenses && npm run format:fix",
"version": "npm run changelog && npm run licenses && npm run format:fix && git add src/assets/about/CHANGELOG.md && git add src/assets/about/licenses.json",
"prepublishOnly": "npm ci && npm run build && npm run lint && npm run format:check",
"preversion": "npm run prepublishOnly",
"push": "git push && git push origin \"v$npm_package_version\"",
@@ -86,9 +86,9 @@
"@ionic/storage-angular": "3.0.6",
"@ngx-translate/core": "14.0.0",
"@ngx-translate/http-loader": "7.0.0",
"@openstapps/api": "0.45.0",
"@openstapps/configuration": "0.33.0",
"@openstapps/core": "0.72.0",
"@openstapps/api": "1.0.1",
"@openstapps/configuration": "0.34.0",
"@openstapps/core": "1.0.1",
"@transistorsoft/capacitor-background-fetch": "1.0.2",
"capacitor-secure-storage-plugin": "0.8.1",
"cordova-plugin-calendar": "5.1.6",

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Observable} from 'rxjs';
/**
*
*/
export function fromMutationObserver(
target: Node,
options?: MutationObserverInit,
): Observable<MutationRecord[]> {
return new Observable(subscriber => {
const observer = new MutationObserver(mutations => {
subscriber.next(mutations);
});
observer.observe(target, options);
return () => {
observer.disconnect();
};
});
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
// these are the ionic values
export const iosEasing = 'cubic-bezier(0.32,0.72,0,1)';
export const iosDuration = 540;
export const mdEasing = 'cubic-bezier(0.36,0.66,0.04,1)';
export const mdDuration = 280;

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {AnimationBuilder, AnimationController} from '@ionic/angular';
import {AnimationOptions} from '@ionic/angular/providers/nav-controller';
import {iosDuration, iosEasing, mdDuration, mdEasing} from './easings';
/**
*
*/
export function fabExpand(animationController: AnimationController): AnimationBuilder {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (_baseElement: HTMLElement, options: AnimationOptions | any) => {
const rootTransition = animationController
.create()
.duration(options.duration ?? (options.mode === 'ios' ? iosDuration : mdDuration * 1.4))
.easing(options.mode === 'ios' ? iosEasing : mdEasing);
const back = options.direction === 'back';
const fabView = back ? options.enteringEl! : options.leavingEl!;
const otherView = back ? options.leavingEl! : options.enteringEl!;
const fab = fabView.querySelector('ion-fab-button').shadowRoot.querySelector('.button-native');
const fabBounds = fab.getBoundingClientRect();
const viewBounds = otherView.getBoundingClientRect();
const useReducedMotion = viewBounds.width > 500;
const reducedMotionTransform = `${Math.min(viewBounds.width * 0.3, 200)}px`;
const reducedMotionViewBorderRadius = '128px';
const reducedMotionFabGrow = '2';
const reducedMotionViewShrink = '0.9';
const viewCenterX = (viewBounds.width - viewBounds.x) / 2;
const viewCenterY = (viewBounds.height - viewBounds.y) / 2;
const viewOnFab = useReducedMotion
? `translate(${reducedMotionTransform}, ${reducedMotionTransform}) scale(${reducedMotionViewShrink})`
: `translate(${(fabBounds.x - viewBounds.x) / 2}px, ${(fabBounds.y - viewBounds.y) / 2}px) scale(${
fabBounds.width / viewBounds.width
}, ${fabBounds.height / viewBounds.height})`;
const fabOnView = useReducedMotion
? `translate(-${reducedMotionTransform}, -${reducedMotionTransform}) scale(${reducedMotionFabGrow})`
: `translate(${viewCenterX - fabBounds.x}px, ${viewCenterY - fabBounds.y}px) scale(${
viewBounds.width / fabBounds.width
}, ${viewBounds.height / fabBounds.height})`;
const transformNormal = `translate(0px, 0px) scale(1, 1)`;
const viewBorderRadius = useReducedMotion ? reducedMotionViewBorderRadius : '50%';
const fabViewFade = animationController
.create()
.beforeStyles({zIndex: -1})
.fromTo('opacity', '1', '1')
.addElement(fabView);
const fabGrow = animationController
.create()
.beforeStyles({transformOrigin: 'center'})
.fromTo('transform', back ? fabOnView : transformNormal, back ? transformNormal : fabOnView)
.fromTo('opacity', back ? '0' : '1', back ? '1' : '0')
.fromTo('borderRadius', back ? '0' : '50%', back ? '50%' : '0')
.addElement(fab);
const viewGrow = animationController
.create()
.beforeStyles({zIndex: 200, overflow: 'hidden', transformOrigin: 'center'})
.fromTo('transform', back ? transformNormal : viewOnFab, back ? viewOnFab : transformNormal)
.fromTo('opacity', back ? '1' : '0', back ? '0' : '1')
.fromTo('borderRadius', back ? '0' : viewBorderRadius, back ? viewBorderRadius : '0')
.addElement(otherView);
return rootTransition.addAnimation(fabGrow).addAnimation(viewGrow).addAnimation(fabViewFade);
};
}

View File

@@ -1,3 +1,22 @@
<!--
~ Copyright (C) 2023 StApps
~ This program is free software: you can redistribute it and/or modify it
~ under the terms of the GNU General Public License as published by the Free
~ Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful, but WITHOUT
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
~ more details.
~
~ You should have received a copy of the GNU General Public License along with
~ this program. If not, see <https://www.gnu.org/licenses/>.
-->
<<<<<<<< HEAD:frontend/app/src/app/util/simple-swiper.html
<ng-content></ng-content>
========
<ion-app style="margin-top: env(safe-area-inset-top)">
<stapps-navigation></stapps-navigation>
</ion-app>
>>>>>>>> app/develop:frontend/app/src/app/app.component.html

View File

@@ -22,10 +22,8 @@
<!-- TODO: translation -->
</ion-toolbar>
</ion-header>
<ion-content class="ion-content-parallax">
<div>
<div class="about-changelog">
<markdown src="assets/about/CHANGELOG.md"></markdown>
</div>
<ion-content parallax>
<div class="about-changelog">
<markdown src="assets/about/CHANGELOG.md"></markdown>
</div>
</ion-content>

View File

@@ -22,32 +22,30 @@
<!-- TODO: translation -->
</ion-toolbar>
</ion-header>
<ion-content class="ion-content-parallax">
<div>
<div class="licenses-content">
<ion-card
*ngFor="let license of licenses"
[href]="license.url || license.repository"
rel="external"
target="_blank"
>
<ion-card-header>
<ion-card-title>
{{ license.name }}
<ion-icon size="16" weight="300" class="supertext-icon" name="open_in_browser"></ion-icon>
</ion-card-title>
<ion-content parallax>
<div class="licenses-content">
<ion-card
*ngFor="let license of licenses"
[href]="license.url || license.repository"
rel="external"
target="_blank"
>
<ion-card-header>
<ion-card-title>
{{ license.name }}
<ion-icon size="16" weight="300" class="supertext-icon" name="open_in_browser"></ion-icon>
</ion-card-title>
<ion-card-subtitle *ngIf="license.authors || license.publisher">
{{ license.authors || license.publisher }}
</ion-card-subtitle>
</ion-card-header>
<ion-card-content>
<ion-chip (click)="$event.preventDefault(); viewLicense(license)">
<ion-icon name="copyright"></ion-icon>
<ion-label>{{ license.licenses }} License</ion-label>
</ion-chip>
</ion-card-content>
</ion-card>
</div>
<ion-card-subtitle *ngIf="license.authors || license.publisher">
{{ license.authors || license.publisher }}
</ion-card-subtitle>
</ion-card-header>
<ion-card-content>
<ion-chip (click)="$event.preventDefault(); viewLicense(license)">
<ion-icon name="copyright"></ion-icon>
<ion-label>{{ license.licenses }} License</ion-label>
</ion-chip>
</ion-card-content>
</ion-card>
</div>
</ion-content>

View File

@@ -1,5 +1,10 @@
<<<<<<<< HEAD:packages/es-mapping-generator/test/mapping-model/aggregations/src/types.ts
/*
* Copyright (C) 2020 StApps
========
/*!
* Copyright (C) 2023 StApps
>>>>>>>> app/develop:frontend/app/src/app/modules/about/about-licenses.scss
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -13,6 +18,17 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
<<<<<<<< HEAD:packages/es-mapping-generator/test/mapping-model/aggregations/src/types.ts
export enum ThingType {
AggArray = 'agg array',
AggGlobal = 'agg global',
AggGlobalNested = 'agg global nested',
AggNested = 'agg nested',
AggInherited = 'agg inherited',
AggInheritedGlobal = 'agg inherited global',
AggInheritedOverwritten = 'agg inherited overwritten',
}
========
ion-content > div {
height: 100%;
}
@@ -22,7 +38,7 @@ cdk-virtual-scroll-viewport {
width: 100%;
}
::ng-deep {
:host ::ng-deep {
.cdk-virtual-scroll-content-wrapper {
width: 100%;
}
@@ -36,3 +52,4 @@ cdk-virtual-scroll-viewport {
vertical-align: text-top;
height: 14px;
}
>>>>>>>> app/develop:frontend/app/src/app/modules/about/about-licenses.scss

View File

@@ -1,5 +1,5 @@
<!--
~ Copyright (C) 2022 StApps
~ Copyright (C) 2023 StApps
~ This program is free software: you can redistribute it and/or modify it
~ under the terms of the GNU General Public License as published by the Free
~ Software Foundation, version 3.
@@ -36,7 +36,7 @@
</ion-col>
</ion-row>
</ion-grid>
<ion-item *ngSwitchCase="'router link'" [routerLink]="content.link">
<ion-item *ngSwitchCase="'router link'" [routerLink]="content.link" fill="clear">
<ion-icon *ngIf="content.icon" [name]="content.icon" slot="start"></ion-icon>
<ion-label>{{ 'title' | translateSimple: content }}</ion-label>
</ion-item>

View File

@@ -24,11 +24,9 @@
</ng-template>
</ion-toolbar>
</ion-header>
<ion-content *ngIf="content" class="ion-content-parallax">
<div>
<ion-text color="light">{{ appName }} v{{ version }}</ion-text>
<div class="page-content">
<about-page-content *ngFor="let element of content.content" [content]="element"></about-page-content>
</div>
<ion-content parallax *ngIf="content">
<ion-text>{{ appName }} v{{ version }}</ion-text>
<div class="page-content">
<about-page-content *ngFor="let element of content.content" [content]="element"></about-page-content>
</div>
</ion-content>

View File

@@ -62,10 +62,7 @@ ion-text {
display: flex;
flex-direction: column;
gap: var(--spacing-sm);
background: var(--ion-color-primary-contrast);
&.licenses-content {
background: var(--ion-color-light);
}
background: var(--ion-item-background);
padding-block-end: var(--spacing-md);
@include border-radius-in-parallax(var(--border-radius-default));
@@ -80,7 +77,6 @@ ion-text {
@include border-radius-in-parallax(var(--border-radius-default));
overflow: hidden;
position: relative;
background-color: var(--ion-color-primary-contrast);
margin: 0;
& > ion-thumbnail {
@@ -89,3 +85,7 @@ ion-text {
}
}
}
ion-text {
color: var(--ion-color-primary-contrast);
}

View File

@@ -1,19 +1,20 @@
/*!
* Copyright (C) 2022 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
.content {
height: 100%;
padding-inline: 8px;
--ion-item-background: var(--ion-background-color);
}

View File

@@ -29,6 +29,7 @@ import {StorageProvider} from '../storage/storage.provider';
import {DefaultAuthService} from './default-auth.service';
import {PAIAAuthService} from './paia/paia-auth.service';
import {SimpleBrowser} from '../../util/browser.factory';
import {AlertController} from '@ionic/angular';
const AUTH_ORIGIN_PATH = 'stapps.auth.origin_path';
@@ -45,6 +46,7 @@ export class AuthHelperService {
private defaultAuth: DefaultAuthService,
private paiaAuth: PAIAAuthService,
private browser: SimpleBrowser,
private alertController: AlertController,
) {
this.userConfigurationMap = (
this.configProvider.getAnyValue('auth') as {
@@ -118,11 +120,33 @@ export class AuthHelperService {
/**
* Ends browser session by opening endSessionEndpoint URL of the provider
*
* @param providerType Type of the provider (e.g. 'default' or 'paia')
*/
async endBrowserSession(providerType: SCAuthorizationProviderType) {
const endSessionEndpoint = (await this.getProvider(providerType).configuration).endSessionEndpoint ?? '';
if (endSessionEndpoint.length > 0) {
this.browser.open(new URL(endSessionEndpoint).href);
const endSessionEndpoint = (await this.getProvider(providerType).configuration).endSessionEndpoint;
if (endSessionEndpoint) {
const alert: HTMLIonAlertElement = await this.alertController.create({
header: this.translateService.instant(`auth.messages.${providerType}.log_out_alert.header`),
message: this.translateService.instant(`auth.messages.${providerType}.log_out_alert.message`),
buttons: [
{
text: this.translateService.instant('no'),
cssClass: 'default',
},
{
text: this.translateService.instant('yes'),
role: 'confirm',
cssClass: 'preferred',
handler: () => {
this.browser.open(new URL(endSessionEndpoint).href);
},
},
],
});
await alert.present();
}
}
}

View File

@@ -1,5 +1,5 @@
/*!
* Copyright (C) 2022 StApps
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -27,3 +27,7 @@ ion-toolbar.in-toolbar {
padding: 0 !important;
}
}
:host {
--ion-item-background: var(--ion-backgrund-color);
}

View File

@@ -117,8 +117,9 @@ export class CatalogComponent implements OnInit, OnDestroy {
semester => semester.startDate <= today && semester.endDate > today,
);
const currentSemesterIndex = semesters.findIndex(semester => semester.uid === currentSemester?.uid);
this.availableSemesters = semesters.slice(currentSemesterIndex - 1, currentSemesterIndex + 2).reverse();
this.availableSemesters = semesters
.slice(Math.max(0, currentSemesterIndex - 1), Math.min(currentSemesterIndex + 2, semesters.length))
.reverse();
if (typeof this.activeSemester !== 'undefined') {
return;
}

View File

@@ -121,11 +121,11 @@ export class CatalogProvider {
arguments: {
bounds: {
lowerBound: {
limit: `${new Date().setFullYear(new Date().getFullYear() - 1)}`,
limit: `${new Date(new Date().setFullYear(new Date().getFullYear() - 1)).toISOString()}`,
mode: 'inclusive',
},
upperBound: {
limit: `${new Date().setFullYear(new Date().getFullYear() + 1)}`,
limit: `${new Date(new Date().setFullYear(new Date().getFullYear() + 1)).toISOString()}`,
mode: 'inclusive',
},
},

View File

@@ -48,14 +48,18 @@
}
ion-content {
--background: var(--ion-color-light);
--background: var(--ion-background-color);
--padding-bottom: var(--spacing-xl);
&::part(inner-scroll) {
scrollbar-gutter: stable;
}
}
.schedule {
width: 100%;
z-index: 3;
background: var(--ion-color-primary);
color: var(--ion-color-primary-contrast);
display: flex;
justify-content: space-between;
gap: var(--spacing-md);

View File

@@ -23,7 +23,6 @@ import {MomentModule} from 'ngx-moment';
import {DataModule} from '../data/data.module';
import {SettingsProvider} from '../settings/settings.provider';
import {DashboardComponent} from './dashboard.component';
import {EditModalComponent} from './edit-modal/edit-modal.component';
import {SearchSectionComponent} from './sections/search-section/search-section.component';
import {NewsSectionComponent} from './sections/news-section/news-section.component';
import {MensaSectionComponent} from './sections/mensa-section/mensa-section.component';
@@ -46,7 +45,6 @@ const catalogRoutes: Routes = [
*/
@NgModule({
declarations: [
EditModalComponent,
SearchSectionComponent,
NewsSectionComponent,
MensaSectionComponent,
@@ -69,6 +67,5 @@ const catalogRoutes: Routes = [
NewsModule,
],
providers: [SettingsProvider, TranslatePipe],
exports: [EditModalComponent],
})
export class DashboardModule {}

View File

@@ -1,52 +0,0 @@
<!--
~ Copyright (C) 2022 StApps
~ This program is free software: you can redistribute it and/or modify it
~ under the terms of the GNU General Public License as published by the Free
~ Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful, but WITHOUT
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
~ more details.
~
~ You should have received a copy of the GNU General Public License along with
~ this program. If not, see <https://www.gnu.org/licenses/>.
-->
<ion-header>
<ion-toolbar mode="ios">
<ion-title>{{ 'modal.settings' | translate | titlecase }}</ion-title>
<ion-button fill="clear" slot="start" (click)="dismissModal()">
{{ 'modal.DISMISS_CANCEL' | translate }}
</ion-button>
<ion-button fill="clear" slot="end" (click)="onSaveClick()">
<ion-label>{{ 'modal.DISMISS_CONFIRM' | translate }}</ion-label>
</ion-button>
</ion-toolbar>
</ion-header>
<ion-content>
<ng-container [ngSwitch]="true">
<ion-reorder-group
*ngSwitchCase="type === types.CHECKBOXES"
disabled="false"
(ionItemReorder)="doReorder($event)"
>
<!-- Default reorder icon, end aligned items -->
<ion-item *ngFor="let item of items">
<ion-reorder slot="start"></ion-reorder>
<ion-label>{{ item.labelLocalized }}</ion-label>
<ion-toggle slot="end" [checked]="item.active" [(ngModel)]="item.active"></ion-toggle>
</ion-item>
</ion-reorder-group>
<ion-radio-group *ngSwitchCase="type === types.RADIOBOXES" [(ngModel)]="selectedValue">
<ion-list-header>
<ion-label>{{ 'dashboard.canteens.choose_favorite' | translate }}</ion-label>
</ion-list-header>
<ion-item *ngFor="let item of items">
<ion-label>{{ item.labelLocalized }}</ion-label>
<ion-radio slot="end" [value]="item.id"></ion-radio>
</ion-item>
</ion-radio-group>
</ng-container>
</ion-content>

View File

@@ -1,3 +0,0 @@
:host {
--width: 100vw;
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright (C) 2022 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Component, Input, OnInit, ViewChild} from '@angular/core';
import {IonReorderGroup, ModalController} from '@ionic/angular';
import {ItemReorderEventDetail} from '@ionic/core';
import {EditModalItem, EditModalTypeEnum} from './edit-modal-type.enum';
/**
* Shows a modal window to sort and enable/disable menu items
*/
@Component({
selector: 'stapps-dashboard-edit-modal',
templateUrl: 'edit-modal.component.html',
styleUrls: ['edit-modal.component.scss'],
})
export class EditModalComponent implements OnInit {
@ViewChild(IonReorderGroup) reorderGroup: IonReorderGroup;
@Input() type: EditModalTypeEnum = EditModalTypeEnum.CHECKBOXES;
@Input() items: EditModalItem[];
@Input() selectedValue: string;
reorderedItems: EditModalItem[];
types = EditModalTypeEnum;
constructor(public modalController: ModalController) {}
ngOnInit() {
this.reorderedItems = this.items;
}
ionViewWillLeave() {
this.dismissModal();
}
doReorder(event: CustomEvent<ItemReorderEventDetail>) {
this.reorderedItems = event.detail.complete(this.reorderedItems);
}
onSaveClick() {
this.modalController.dismiss({
items: this.reorderedItems,
selectedValue: this.selectedValue,
});
}
dismissModal() {
this.modalController.dismiss();
}
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {animate, style, transition, trigger} from '@angular/animations';
export const fadeAnimation = trigger('fade', [
transition(':enter', [style({opacity: '0'}), animate(250, style({opacity: '1'}))]),
]);

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {SCBuildingCategories, SCThings, SCThingWithCategories} from '@openstapps/core';
const mensaCategories = new Set<SCBuildingCategories>(['canteen', 'cafe', 'student canteen', 'restaurant']);
/**
*
*/
export function isMensaThing(item: SCThings): boolean {
return (
(item as SCThingWithCategories<string, never>).categories?.some(category =>
mensaCategories.has(category as never),
) || false
);
}

View File

@@ -17,9 +17,9 @@
<ion-button slot="button-end" fill="clear" color="medium" [routerLink]="['/favorites']">
<ion-icon slot="icon-only" name="search" size="24"></ion-icon>
</ion-button>
<simple-swiper *ngIf="(items | async)?.length; else noItems">
<simple-swiper *ngIf="items | async as items; else noItems" @fade>
<stapps-data-list-item
*ngFor="let item of items | async"
*ngFor="let item of items"
[hideThumbnail]="true"
[favoriteButton]="false"
[item]="item"

View File

@@ -12,22 +12,11 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {AlertController, AnimationController} from '@ionic/angular';
import {combineLatest} from 'rxjs';
import {debounceTime, distinctUntilChanged, startWith, take} from 'rxjs/operators';
import {NGXLogger} from 'ngx-logger';
import {SCThings} from '@openstapps/core';
import {DataProvider} from '../../../data/data.provider';
import {DataRoutingService} from '../../../data/data-routing.service';
import {SearchPageComponent} from '../../../data/list/search-page.component';
import {PositionService} from '../../../map/position.service';
import {SettingsProvider} from '../../../settings/settings.provider';
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {filter, map} from 'rxjs/operators';
import {FavoritesService} from '../../../favorites/favorites.service';
import {ContextMenuService} from '../../../menu/context/context-menu.service';
import {ConfigProvider} from '../../../config/config.provider';
import {fadeAnimation} from '../../fade.animation';
import {isMensaThing} from '../../mensa-filters';
/**
* Shows a section with meals of the chosen mensa
@@ -36,95 +25,14 @@ import {ConfigProvider} from '../../../config/config.provider';
selector: 'stapps-favorites-section',
templateUrl: 'favorites-section.component.html',
styleUrls: ['favorites-section.component.scss'],
animations: [fadeAnimation],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FavoritesSectionComponent extends SearchPageComponent implements OnInit {
constructor(
protected readonly alertController: AlertController,
protected dataProvider: DataProvider,
protected readonly contextMenuService: ContextMenuService,
protected readonly settingsProvider: SettingsProvider,
protected readonly logger: NGXLogger,
protected dataRoutingService: DataRoutingService,
protected router: Router,
route: ActivatedRoute,
positionService: PositionService,
private favoritesService: FavoritesService,
configProvider: ConfigProvider,
animationController: AnimationController,
) {
super(
alertController,
dataProvider,
contextMenuService,
settingsProvider,
logger,
dataRoutingService,
router,
route,
positionService,
configProvider,
animationController,
);
}
export class FavoritesSectionComponent {
items = this.favoritesService.favoriteThings$.pipe(
map(favorites => favorites.filter(it => !isMensaThing(it))),
filter(favorites => favorites.length > 0),
);
async initialize() {
this.subscriptions.push(
combineLatest([
this.queryTextChanged.pipe(
debounceTime(this.searchQueryDueTime),
distinctUntilChanged(),
startWith(this.queryText),
),
this.favoritesService.favoritesChanged$,
]).subscribe(async () => {
await this.fetchAndUpdateItems();
this.queryChanged.next();
}),
);
}
/**
* Fetches/updates the favorites (search page component's method override)
*/
async fetchAndUpdateItems() {
this.favoritesService
.search(this.queryText, this.filterQuery, this.sortQuery)
.pipe(take(1))
.subscribe(result => {
this.items = new Promise(resolve => {
resolve(result.data && result.data.filter(item => !this.isMensaThing(item)));
});
});
}
/**
* Helper function as 'typeof' is not accessible in HTML
*
* @param item TODO
*/
isMensaThing(item: SCThings): boolean {
return (
this.hasCategories(item) &&
((item.categories as string[]).includes('canteen') ||
(item.categories as string[]).includes('cafe') ||
(item.categories as string[]).includes('student canteen') ||
(item.categories as string[]).includes('restaurant'))
);
}
/**
* TODO
*
* @param item TODO
*/
hasCategories(item: SCThings): item is SCThings & {categories: string[]} {
return typeof (item as {categories: string[]}).categories !== 'undefined';
}
/**
* Emit event that an item was selected
*/
notifySelect(item: SCThings) {
this.dataRoutingService.emitChildEvent(item);
}
constructor(private favoritesService: FavoritesService) {}
}

View File

@@ -12,11 +12,11 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Component, Input} from '@angular/core';
import {ChangeDetectionStrategy, Component, Input} from '@angular/core';
import {SCDish, SCPlace, SCThings} from '@openstapps/core';
import {PlaceMensaService} from '../../../data/types/place/special/mensa/place-mensa-service';
import {animate, style, transition, trigger} from '@angular/animations';
import moment from 'moment';
import {fadeAnimation} from '../../fade.animation';
/**
* Shows a section with meals of the chosen mensa
@@ -25,11 +25,8 @@ import moment from 'moment';
selector: 'stapps-mensa-section-content',
templateUrl: 'mensa-section-content.component.html',
styleUrls: ['mensa-section-content.component.scss'],
animations: [
trigger('fade', [
transition(':enter', [style({opacity: '0'}), animate('500ms ease', style({opacity: '1'}))]),
]),
],
animations: [fadeAnimation],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MensaSectionContentComponent {
/**

View File

@@ -30,7 +30,7 @@
<ion-item class="nothing-selected" lines="none">
<ion-label class="ion-text-wrap">
{{ 'dashboard.canteens.no_favorite_prefix' | translate }}
<a (click)="onSectionEdit()">{{ 'dashboard.canteens.no_favorite_link' | translate }}</a>
<a [routerLink]="['/canteen']">{{ 'dashboard.canteens.no_favorite_link' | translate }}</a>
{{ 'dashboard.canteens.no_favorite_suffix' | translate }}
</ion-label>
</ion-item>

View File

@@ -12,23 +12,11 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Component} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {AlertController, AnimationController, ModalController} from '@ionic/angular';
import {combineLatest, Subscription} from 'rxjs';
import {debounceTime, distinctUntilChanged, startWith, take} from 'rxjs/operators';
import {NGXLogger} from 'ngx-logger';
import {SCThings} from '@openstapps/core';
import {DataProvider} from '../../../data/data.provider';
import {DataRoutingService} from '../../../data/data-routing.service';
import {FoodDataListComponent} from '../../../data/list/food-data-list.component';
import {PositionService} from '../../../map/position.service';
import {SettingsProvider} from '../../../settings/settings.provider';
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {map} from 'rxjs/operators';
import {FavoritesService} from '../../../favorites/favorites.service';
import {ContextMenuService} from '../../../menu/context/context-menu.service';
import {ConfigProvider} from '../../../config/config.provider';
import {animate, style, transition, trigger} from '@angular/animations';
import {fadeAnimation} from '../../fade.animation';
import {isMensaThing} from '../../mensa-filters';
/**
* Shows a section with meals of the chosen mensa
@@ -37,107 +25,11 @@ import {animate, style, transition, trigger} from '@angular/animations';
selector: 'stapps-mensa-section',
templateUrl: 'mensa-section.component.html',
styleUrls: ['mensa-section.component.scss'],
animations: [
trigger('fade', [transition(':enter', [style({opacity: '0'}), animate(250, style({opacity: '1'}))])]),
],
animations: [fadeAnimation],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MensaSectionComponent extends FoodDataListComponent {
sub: Subscription;
export class MensaSectionComponent {
items = this.favoritesService.favoriteThings$.pipe(map(favorites => favorites.filter(isMensaThing)));
constructor(
protected readonly alertController: AlertController,
protected dataProvider: DataProvider,
protected readonly contextMenuService: ContextMenuService,
protected readonly settingsProvider: SettingsProvider,
protected readonly logger: NGXLogger,
protected dataRoutingService: DataRoutingService,
protected router: Router,
route: ActivatedRoute,
protected positionService: PositionService,
public modalController: ModalController,
protected favoritesService: FavoritesService,
configProvider: ConfigProvider,
animationController: AnimationController,
) {
super(
alertController,
dataProvider,
contextMenuService,
settingsProvider,
logger,
dataRoutingService,
router,
route,
positionService,
configProvider,
animationController,
);
}
async initialize() {
super.initialize();
this.subscriptions.push(
combineLatest([
this.queryTextChanged.pipe(
debounceTime(this.searchQueryDueTime),
distinctUntilChanged(),
startWith(this.queryText),
),
this.favoritesService.favoritesChanged$,
]).subscribe(async query => {
this.queryText = query[0];
this.from = 0;
if (typeof this.filterQuery !== 'undefined' || this.queryText?.length > 0 || this.showDefaultData) {
await this.fetchAndUpdateItems();
this.queryChanged.next();
}
}),
);
}
/**
* Fetches/updates the favorites (search page component's method override)
*/
async fetchAndUpdateItems() {
this.favoritesService
.search(this.queryText, this.filterQuery, this.sortQuery)
.pipe(take(1))
.subscribe(result => {
this.items = new Promise(resolve => {
resolve(result.data && result.data.filter(item => this.isMensaThing(item)));
});
});
}
/**
* Helper function as 'typeof' is not accessible in HTML
*
* @param item TODO
*/
isMensaThing(item: SCThings): boolean {
return (
this.hasCategories(item) &&
((item.categories as string[]).includes('canteen') ||
(item.categories as string[]).includes('cafe') ||
(item.categories as string[]).includes('student canteen') ||
(item.categories as string[]).includes('restaurant'))
);
}
/**
* TODO
*
* @param item TODO
*/
hasCategories(item: SCThings): item is SCThings & {categories: string[]} {
return typeof (item as {categories: string[]}).categories !== 'undefined';
}
/**
* Action when user clicked edit to this section
*/
onSectionEdit() {
void this.router.navigate(['/canteen']);
}
constructor(protected favoritesService: FavoritesService) {}
}

View File

@@ -13,12 +13,36 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
<<<<<<<< HEAD:frontend/app/src/app/modules/data/list/data-list-item.scss
:host {
display: block;
}
ion-item::part(native) {
height: 100%;
}
.ion-text-wrap ::ng-deep ion-label {
white-space: normal !important;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
}
ion-item {
--border-color: transparent;
@include border-radius-in-parallax(var(--border-radius-default));
overflow: hidden;
--inner-padding-end: 0;
--padding-start: var(--spacing-sm);
margin: var(--spacing-sm);
========
simple-swiper {
--swiper-slide-width: 256px;
}
>>>>>>>> app/develop:frontend/app/src/app/modules/dashboard/sections/news-section/news-section.component.scss
.more-news {
width: 128px;
font-size: var(--font-size-xl);
--color: var(--ion-color-medium-tint);
@@ -43,3 +67,43 @@ simple-swiper {
width: 200px;
}
}
:host.square ::ng-deep {
ion-item {
margin: 0;
}
ion-row {
flex-direction: column;
justify-content: space-between;
height: 120px;
}
ion-col {
flex-grow: 0;
flex-basis: min-content;
}
.title {
display: -webkit-box;
white-space: break-spaces;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}
.title-sub {
display: none;
}
// fix for Safari
stapps-offers-in-list {
position: absolute;
bottom: 0;
right: 0;
}
stapps-offers-in-list .place {
display: none;
}
}

View File

@@ -12,11 +12,9 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Component} from '@angular/core';
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {NewsProvider} from '../../../news/news.provider';
import {SCMessage} from '@openstapps/core';
import {animate, style, transition, trigger} from '@angular/animations';
import {Router} from '@angular/router';
import {fadeAnimation} from '../../fade.animation';
/**
* Shows a section with news
@@ -25,21 +23,14 @@ import {Router} from '@angular/router';
selector: 'stapps-news-section',
templateUrl: 'news-section.component.html',
styleUrls: ['news-section.component.scss'],
animations: [
trigger('fade', [
transition(':enter', [
style({opacity: '0', transform: 'translateX(100px)'}),
animate('250ms ease', style({opacity: '1', transform: 'translateX(0)'})),
]),
]),
],
animations: [fadeAnimation],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NewsSectionComponent {
news: Promise<SCMessage[]>;
news = this.newsProvider
.getCurrentFilters()
// eslint-disable-next-line unicorn/prefer-top-level-await,unicorn/consistent-function-scoping
.then(filters => this.newsProvider.getList(5, 0, filters));
constructor(readonly newsProvider: NewsProvider, readonly router: Router) {
this.news = this.newsProvider
.getCurrentFilters()
.then(filters => this.newsProvider.getList(5, 0, filters));
}
constructor(readonly newsProvider: NewsProvider) {}
}

View File

@@ -13,12 +13,24 @@
~ this program. If not, see <https://www.gnu.org/licenses/>.
-->
<<<<<<<< HEAD:frontend/app/src/app/modules/data/list/data-list-item-host-default.html
<h2>
{{ 'name' | thingTranslate: item }}
</h2>
<p *ngIf="item.description">
<stapps-long-inline-text
[text]="'description' | thingTranslate: item"
[size]="80"
></stapps-long-inline-text>
</p>
========
<stapps-section title="{{ 'dashboard.navigation.item.search' | translate }}">
<ion-searchbar
[routerLink]="'/search'"
[routerLink]="['/search']"
[routerAnimation]="routeTransition"
class="stapps-searchbar ion-activatable ripple-parent"
>
<ion-ripple-effect></ion-ripple-effect>
</ion-searchbar>
</stapps-section>
>>>>>>>> app/develop:frontend/app/src/app/modules/dashboard/sections/search-section/search-section.component.html

View File

@@ -13,10 +13,7 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Component} from '@angular/core';
import {Router} from '@angular/router';
import {Capacitor} from '@capacitor/core';
import {Keyboard} from '@capacitor/keyboard';
import {AnimationBuilder, AnimationController} from '@ionic/angular';
import {AnimationController} from '@ionic/angular';
import {homePageSearchTransition} from './search-route-transition';
/**
@@ -28,29 +25,7 @@ import {homePageSearchTransition} from './search-route-transition';
styleUrls: ['search-section.component.scss'],
})
export class SearchSectionComponent {
searchTerm = '';
routeTransition = homePageSearchTransition(this.animationController);
routeTransition: AnimationBuilder;
constructor(private router: Router, private animationController: AnimationController) {
this.routeTransition = homePageSearchTransition(this.animationController);
}
/**
* User submits search
*/
onSubmitSearch() {
this.router
.navigate(['/search'], {queryParams: {query: this.searchTerm}})
.then(() => this.hideKeyboard());
}
/**
* Hides keyboard in native app environments
*/
hideKeyboard() {
if (Capacitor.isNativePlatform()) {
Keyboard.hide();
}
}
constructor(private animationController: AnimationController) {}
}

View File

@@ -1,5 +1,5 @@
<!--
~ Copyright (C) 2022 StApps
~ Copyright (C) 2023 StApps
~ This program is free software: you can redistribute it and/or modify it
~ under the terms of the GNU General Public License as published by the Free
~ Software Foundation, version 3.
@@ -26,14 +26,12 @@
<ion-label>{{ label | translate }}</ion-label>
<stapps-edit-modal #editModal (save)="selection.save()">
<ng-template>
<ion-content class="ion-padding modal-content">
<div>
<stapps-edit-event-selection
#selection
[items]="associatedDateSeries"
(modified)="editModal.pendingChanges = true"
></stapps-edit-event-selection>
</div>
<ion-content parallax [parallaxSize]="160" class="ion-padding modal-content">
<stapps-edit-event-selection
#selection
[items]="associatedDateSeries"
(modified)="editModal.pendingChanges = true"
></stapps-edit-event-selection>
</ion-content>
<ion-footer mode="ios">
<ion-toolbar color="light">

View File

@@ -12,8 +12,6 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
@import 'src/theme/common/ion-content-parallax';
:host {
display: block;
padding: var(--spacing-sm);
@@ -35,10 +33,7 @@
}
.modal-content {
--background: var(--ion-color-primary);
--color: var(--ion-color-primary-contrast);
@include ion-content-parallax($content-size: 160px);
}
ion-footer > ion-toolbar {

View File

@@ -1,5 +1,5 @@
<!--
~ Copyright (C) 2022 StApps
~ Copyright (C) 2023 StApps
~ This program is free software: you can redistribute it and/or modify it
~ under the terms of the GNU General Public License as published by the Free
~ Software Foundation, version 3.
@@ -38,20 +38,39 @@
*ngFor="let date of frequency.children"
(click)="modified.emit(); date.selected = !date.selected; frequency.notifyChildChanged()"
>
<ion-label *ngIf="date.item.dates.length > 1; else single_event" class="ion-text-wrap">
{{ date.item.duration | amDuration: 'hours' }}
{{ 'data.chips.add_events.popover.AT' | translate }}
{{ date.item.dates[0] | amDateFormat: 'HH:mm ddd' }}
{{ 'data.chips.add_events.popover.UNTIL' | translate }}
{{ date.item.dates[date.item.dates.length - 1] | amDateFormat: 'll' }}
<ion-label>
<ng-container *ngIf="date.item.dates.length > 1; else single_event">
<ion-text>
{{ date.item.dates[0] | amDateFormat: 'dddd, LT' }} -
{{ date.item.dates[0] | amAdd: date.item.duration | amDateFormat: 'LT' }}
</ion-text>
<br />
<ion-text>
{{ date.item.dates[0] | amDateFormat: 'LL' }} -
{{ date.item.dates[date.item.dates.length - 1] | amDateFormat: 'LL' }}
</ion-text>
</ng-container>
<ng-template #single_event>
<ion-text *ngIf="date.item.dates[0] as time; else noDates">
{{ time | amDateFormat: 'LL, LT' }} -
{{ time | amAdd: date.item.duration | amDateFormat: 'LT' }}
</ion-text>
<ng-template #noDates>
<ion-text color="danger">{{ 'data.chips.add_events.popover.DATA_ERROR' | translate }}</ion-text>
<br />
<ion-text *ngFor="let id of date.item.identifiers | keyvalue">
{{ id.key }}: {{ id.value }}
</ion-text>
</ng-template>
</ng-template>
<ng-container class="ion-align-items-center" *ngIf="date.item.inPlace">
<br />
<ion-text color="medium" class="place">
<ion-icon name="pin_drop"></ion-icon>
<span> {{ 'inPlace.name' | thingTranslate: date.item }}</span>
</ion-text>
</ng-container>
</ion-label>
<ng-template #single_event>
<ion-label class="ion-text-wrap">
{{ date.item.duration | amDuration: 'hours' }}
{{ 'data.chips.add_events.popover.AT' | translate }}
{{ date.item.dates[date.item.dates.length - 1] | amDateFormat: 'll, HH:mm' }}
</ion-label>
</ng-template>
<ion-checkbox slot="end" [checked]="date.selected"> </ion-checkbox>
</ion-item>
</ion-list>

View File

@@ -1,5 +1,5 @@
/*!
* Copyright (C) 2022 StApps
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -42,3 +42,8 @@ ion-item-divider.ios > ion-checkbox {
ion-list.md {
padding-top: 0;
}
.place {
display: inline-flex;
align-items: center;
}

View File

@@ -95,10 +95,11 @@ import {BookDetailContentComponent} from './types/book/book-detail-content.compo
import {BookListItemComponent} from './types/book/book-list-item.component';
import {PeriodicalListItemComponent} from './types/periodical/periodical-list-item.component';
import {PeriodicalDetailContentComponent} from './types/periodical/periodical-detail-content.component';
import {SCThingListItemVirtualScrollStrategyDirective} from './list/sc-thing-list-item-virtual-scroll-strategy.directive';
import {DataListItemHostDirective} from './list/data-list-item-host.directive';
import {DataListItemHostDefaultComponent} from './list/data-list-item-host-default.component';
import {browserFactory, SimpleBrowser} from '../../util/browser.factory';
import {DishCharacteristicsComponent} from './types/dish/dish-characteristics.component';
import {SkeletonListComponent} from './list/skeleton-list.component';
/**
* Module for handling data
@@ -111,6 +112,7 @@ import {browserFactory, SimpleBrowser} from '../../util/browser.factory';
AddressDetailComponent,
CatalogDetailContentComponent,
CatalogListItemComponent,
DishCharacteristicsComponent,
DataDetailComponent,
DataDetailContentComponent,
DataIconPipe,
@@ -126,6 +128,7 @@ import {browserFactory, SimpleBrowser} from '../../util/browser.factory';
EventListItemComponent,
FavoriteButtonComponent,
FavoriteDetailContentComponent,
SkeletonListComponent,
FavoriteListItemComponent,
FoodDataListComponent,
LocateActionChipComponent,
@@ -145,7 +148,6 @@ import {browserFactory, SimpleBrowser} from '../../util/browser.factory';
PlaceListItemComponent,
PlaceMensaDetailComponent,
SearchPageComponent,
SCThingListItemVirtualScrollStrategyDirective,
SemesterDetailContentComponent,
SemesterListItemComponent,
DataListItemHostDirective,
@@ -207,7 +209,6 @@ import {browserFactory, SimpleBrowser} from '../../util/browser.factory';
},
],
exports: [
SCThingListItemVirtualScrollStrategyDirective,
DataDetailComponent,
DataDetailContentComponent,
DataIconPipe,

View File

@@ -38,7 +38,7 @@ stapps-origin-detail {
position: relative;
margin-block-start: calc((var(--header-spacing-bottom) - var(--spacing-xl)) * -1);
margin-block-end: var(--spacing-xl);
background-color: var(--ion-color-primary-contrast);
background-color: var(--ion-card-background);
@include content-padding();
& > ion-thumbnail {
@@ -73,7 +73,6 @@ stapps-origin-detail {
padding-bottom: 8px;
}
ion-card-header {
color: var(--ion-color-dark);
padding-top: 8px;
padding-bottom: 4px;
font-weight: bold;

View File

@@ -1,5 +1,5 @@
<!--
~ Copyright (C) 2022 StApps
~ Copyright (C) 2023 StApps
~ This program is free software: you can redistribute it and/or modify it
~ under the terms of the GNU General Public License as published by the Free
~ Software Foundation, version 3.
@@ -30,8 +30,8 @@
</ion-toolbar>
</ion-header>
<ng-content select="[header]"></ng-content>
<ion-content class="ion-no-padding ion-content-parallax">
<div [ngSwitch]="true">
<ion-content parallax class="ion-no-padding">
<ng-container [ngSwitch]="true">
<ng-container *ngSwitchCase="!item && (isDisconnected | async)">
<div class="centeredMessageContainer">
<ion-icon name="signal_disconnected"></ion-icon>
@@ -59,5 +59,5 @@
[contentTemplateRef]="contentTemplateRef"
></stapps-data-detail-content>
</ng-container>
</div>
</ng-container>
</ion-content>

View File

@@ -12,10 +12,21 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
.crumb-label {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
}
ion-breadcrumb {
color: var(--ion-color-primary-contrast);
&::part(separator) {
color: var(--ion-color-primary-contrast);
}
&::part(collapsed-indicator) {
background: var(--ion-color-primary-contrast);
}
}

View File

@@ -1,18 +1,29 @@
<!--
~ Copyright (C) 2022 StApps
~ This program is free software: you can redistribute it and/or modify it
~ under the terms of the GNU General Public License as published by the Free
~ Software Foundation, version 3.
~ Copyright (C) 2023 StApps
~ This program is free software: you can redistribute it and/or modify it
~ under the terms of the GNU General Public License as published by the Free
~ Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful, but WITHOUT
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
~ more details.
~ This program is distributed in the hope that it will be useful, but WITHOUT
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
~ more details.
~
~ You should have received a copy of the GNU General Public License along with
~ this program. If not, see <https://www.gnu.org/licenses/>.
~ You should have received a copy of the GNU General Public License along with
~ this program. If not, see <https://www.gnu.org/licenses/>.
-->
<<<<<<<< HEAD:frontend/app/src/app/modules/dashboard/sections/search-section/search-section.component.html
<stapps-section title="{{ 'dashboard.navigation.item.search' | translate }}">
<ion-searchbar
[routerLink]="'/search'"
[routerAnimation]="routeTransition"
class="stapps-searchbar ion-activatable ripple-parent"
>
<ion-ripple-effect></ion-ripple-effect>
</ion-searchbar>
</stapps-section>
========
<ion-button (click)="toggle($event)" color="medium" size="small" fill="clear">
<ion-icon
slot="icon-only"
@@ -21,3 +32,4 @@
name="grade"
></ion-icon>
</ion-button>
>>>>>>>> app/develop:frontend/app/src/app/modules/data/elements/favorite-button.component.html

View File

@@ -1,5 +1,5 @@
/*!
* Copyright (C) 2022 StApps
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -27,6 +27,7 @@
}
.selected {
// TODO
color: #fbc02d;
}
}

View File

@@ -14,17 +14,17 @@
-->
<div>
<ion-text *ngIf="price && !soldOut">
<ion-text *ngIf="price && !soldOut" style="white-space: nowrap">
<h2>
{{ price | currency: 'EUR':'symbol':undefined:'de' }}
</h2>
</ion-text>
<ion-text *ngIf="soldOut" color="danger">
<ion-text *ngIf="soldOut" color="danger" class="sold-out" style="white-space: nowrap">
<h2>
{{ 'data.detail.offers.sold_out' | translate }}
</h2>
</ion-text>
<p *ngIf="_offers[0].inPlace && !soldOut" class="place">
<p *ngIf="_offers[0].inPlace && !soldOut" class="place" style="white-space: nowrap">
<ion-icon name="pin_drop"></ion-icon>{{ _offers[0].inPlace.name
}}<span *ngIf="_offers.length > 1">...</span>
</p>

View File

@@ -38,7 +38,6 @@
<ion-button
expand="full"
fill="clear"
color="light"
*ngIf="item.description && buttonShown"
(click)="toggleDescriptionAccordion()"
>

View File

@@ -39,10 +39,14 @@ ion-card {
padding: 0 0 var(--header-spacing-bottom);
.description * {
color: var(--ion-color-light);
color: var(--ion-color-primary-contrast);
}
.openingHours {
color: var(--ion-color-light);
color: var(--ion-color-primary-contrast);
}
}
ion-button {
--color: var(--ion-color-primary-contrast);
}
}

View File

@@ -13,6 +13,19 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
<<<<<<<< HEAD:frontend/app/src/app/util/simple-swiper.component.ts
import {Component, ContentChildren, ElementRef, ViewContainerRef} from '@angular/core';
@Component({
selector: 'simple-swiper',
templateUrl: 'simple-swiper.html',
styleUrls: ['simple-swiper.scss'],
})
export class SimpleSwiperComponent {
constructor(readonly viewContainerRef: ViewContainerRef) {}
@ContentChildren('*') children: ElementRef<unknown>;
========
import {Component, Input} from '@angular/core';
import {SCThings} from '@openstapps/core';
@@ -22,4 +35,5 @@ import {SCThings} from '@openstapps/core';
})
export class DataListItemHostDefaultComponent {
@Input() item: SCThings;
>>>>>>>> app/develop:frontend/app/src/app/modules/data/list/data-list-item-host-default.component.ts
}

View File

@@ -13,7 +13,6 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
@import 'src/theme/util/_mixins.scss';
@import 'src/theme/common/_helper.scss';
:host {
display: block;
@@ -44,17 +43,13 @@ ion-item {
ion-label {
width: 100%;
margin-right: 0;
div {
display: flex;
flex-direction: column;
}
}
::ng-deep {
ion-note {
@extend %horizontal-list;
}
}
}
:host.square ::ng-deep {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 StApps
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -12,7 +12,6 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {CdkVirtualScrollViewport} from '@angular/cdk/scrolling';
import {
Component,
ContentChild,
@@ -29,6 +28,7 @@ import {
} from '@angular/core';
import {SCThings} from '@openstapps/core';
import {BehaviorSubject, Observable, Subscription} from 'rxjs';
import {IonInfiniteScroll} from '@ionic/angular';
export interface DataListContext<T> {
$implicit: T;
@@ -43,11 +43,6 @@ export interface DataListContext<T> {
styleUrls: ['data-list.scss'],
})
export class DataListComponent implements OnChanges, OnInit, OnDestroy {
/**
* Amount of list items left to show (in percent) that should trigger a data reload
*/
private readonly reloadThreshold = 0.2;
/**
* All SCThings to display
*/
@@ -86,7 +81,7 @@ export class DataListComponent implements OnChanges, OnInit, OnDestroy {
*/
subscriptions: Subscription[] = [];
@ViewChild(CdkVirtualScrollViewport) viewPort: CdkVirtualScrollViewport;
@ViewChild(IonInfiniteScroll) infiniteScroll: IonInfiniteScroll;
/**
* Signalizes that the data is being loaded
@@ -113,6 +108,7 @@ export class DataListComponent implements OnChanges, OnInit, OnDestroy {
ngOnChanges(changes: SimpleChanges): void {
if (Array.isArray(this.items) && typeof changes.items !== 'undefined') {
this.itemStream.next(this.items);
this.infiniteScroll.complete();
}
}
@@ -127,7 +123,7 @@ export class DataListComponent implements OnChanges, OnInit, OnDestroy {
if (typeof this.resetToTop !== 'undefined') {
this.subscriptions.push(
this.resetToTop.subscribe(() => {
this.viewPort.scrollToIndex(0);
// this.viewPort.scrollToIndex(0);
}),
);
}
@@ -139,18 +135,4 @@ export class DataListComponent implements OnChanges, OnInit, OnDestroy {
notifyLoadMore() {
this.loadMore.emit();
}
/**
* Function to call whenever scroll view visible range changed
*/
scrolled(index: number) {
if (
// first condition prevents "load more" to be executed even before scrolling
index > 0 &&
(this.items?.length ?? 0) - this.viewPort.getRenderedRange().end <=
(this.items?.length ?? 0) * this.reloadThreshold
) {
this.notifyLoadMore();
}
}
}

View File

@@ -15,29 +15,23 @@
<ng-container *ngIf="itemStream | async as items">
<ng-content select="[header]"></ng-content>
<cdk-virtual-scroll-viewport
scThingListItemVirtualScrollStrategy
[style.display]="items && items.length ? 'block' : 'none'"
(loadMore)="notifyLoadMore()"
>
<ng-container *cdkVirtualFor="let item of items; trackBy: identifyItem">
<ion-list [style.display]="items && items.length ? 'block' : 'none'">
<ng-container *ngFor="let item of items">
<ng-container
*ngTemplateOutlet="listItemTemplateRef || defaultListItem; context: {$implicit: item}"
></ng-container>
</ng-container>
</cdk-virtual-scroll-viewport>
<ion-infinite-scroll (ionInfinite)="notifyLoadMore()">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
</ion-list>
</ng-container>
<div [style.display]="!loading && items && items.length === 0 ? 'block' : 'none'">
<ion-label class="centeredMessageContainer">
{{ 'search.nothing_found' | translate | titlecase }}
</ion-label>
</div>
<ion-list [style.display]="loading ? 'block' : 'none'">
<stapps-skeleton-list-item
[hideThumbnail]="singleType"
*ngFor="let skeleton of [].constructor(skeletonItems)"
></stapps-skeleton-list-item>
</ion-list>
<skeleton-list [style.display]="loading ? 'block' : 'none'"></skeleton-list>
<ng-template let-item #defaultListItem>
<stapps-data-list-item [item]="item" [hideThumbnail]="singleType"></stapps-data-list-item>

View File

@@ -1,5 +1,5 @@
/*!
* Copyright (C) 2022 StApps
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -12,18 +12,14 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
<<<<<<<< HEAD:frontend/app/src/app/modules/settings/item/settings-item.scss
========
cdk-virtual-scroll-viewport {
ion-list {
background: none;
}
skeleton-list {
height: 100%;
width: 100%;
}
::ng-deep {
.cdk-virtual-scroll-content-wrapper {
width: 100%;
}
}
.virtual-scroll-expander {
clear: both;
}
>>>>>>>> app/develop:frontend/app/src/app/modules/data/list/data-list.scss

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 StApps
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -12,10 +12,11 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Component} from '@angular/core';
import {Component, OnDestroy, OnInit} from '@angular/core';
import {MapPosition} from '../../map/position.service';
import {SearchPageComponent} from './search-page.component';
import {Geolocation} from '@capacitor/geolocation';
import {Subscription} from 'rxjs';
/**
* Presents a list of places for eating/drinking
@@ -24,15 +25,19 @@ import {Geolocation} from '@capacitor/geolocation';
templateUrl: 'search-page.html',
styleUrls: ['../../data/list/search-page.scss'],
})
export class FoodDataListComponent extends SearchPageComponent {
export class FoodDataListComponent extends SearchPageComponent implements OnInit, OnDestroy {
title = 'canteens.title';
showNavigation = false;
locationWatch?: Subscription;
/**
* Sets the forced filter to present only places for eating/drinking
*/
initialize() {
ngOnInit() {
this.locationWatch?.unsubscribe();
this.locationWatch = this.createLocationWatch();
this.showDefaultData = true;
this.sortQuery = [
@@ -92,29 +97,36 @@ export class FoodDataListComponent extends SearchPageComponent {
},
];
}
super.ngOnInit();
}
private createLocationWatch(): Subscription {
return this.positionService
.watchCurrentLocation(this.constructor.name, {enableHighAccuracy: false, maximumAge: 1000})
.subscribe({
next: (position: MapPosition) => {
this.positionService.position = position;
},
error: async _error => {
this.positionService.position = undefined;
await Geolocation.checkPermissions();
},
});
}
async ionViewWillEnter() {
await super.ionViewWillEnter();
this.subscriptions.push(
this.positionService
.watchCurrentLocation(this.constructor.name, {enableHighAccuracy: false, maximumAge: 1000})
.subscribe({
next: (position: MapPosition) => {
this.positionService.position = position;
},
error: async _error => {
this.positionService.position = undefined;
await Geolocation.checkPermissions();
},
}),
);
this.locationWatch?.unsubscribe();
this.locationWatch = this.createLocationWatch();
}
ionViewWillLeave() {
void this.positionService.clearWatcher(this.constructor.name);
for (const sub of this.subscriptions) {
sub.unsubscribe();
}
this.locationWatch?.unsubscribe();
}
ngOnDestroy() {
super.ngOnDestroy();
this.locationWatch?.unsubscribe();
}
}

View File

@@ -46,7 +46,13 @@ import {searchPageSwitchAnimation} from './search-page-switch-animation';
providers: [ContextMenuService],
})
export class SearchPageComponent implements OnInit, OnDestroy {
title = 'search.title';
@Input() title = 'search.title';
@Input() placeholder = 'search.search_bar.placeholder';
@Input() searchInstruction = 'search.instruction';
@Input() backUrl?: string;
isHebisAvailable = false;

View File

@@ -15,9 +15,9 @@
<stapps-context contentId="data-list"></stapps-context>
<ion-header>
<ion-toolbar color="primary" mode="ios" *ngIf="showDrawer">
<ion-toolbar color="primary" mode="ios" *ngIf="showDrawer && showTopToolbar">
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
<ion-back-button [defaultHref]="backUrl"></ion-back-button>
</ion-buttons>
<ion-title>{{ title | translate }}</ion-title>
</ion-toolbar>
@@ -28,12 +28,12 @@
(search)="hideKeyboard()"
[(ngModel)]="queryText"
showClearButton="always"
placeholder="{{ 'search.search_bar.placeholder' | translate }}"
placeholder="{{ placeholder | translate }}"
mode="md"
type="search"
enterkeyhint="search"
class="filterable"
autofocus
[autofocus]="!showDefaultData"
>
<ion-menu-button menu="context" auto-hide="false">
<ion-icon name="tune"></ion-icon>
@@ -56,9 +56,12 @@
</ion-header>
<ion-content class="content">
<div [style.display]="!showDefaultData && !items && !loading ? 'block' : 'none'">
<div
[class.no-results]="!showDefaultData && !items && !loading"
[style.display]="!showDefaultData && !items && !loading ? 'block' : 'none'"
>
<ion-label class="centeredMessageContainer">
{{ 'search.instruction' | translate }}
{{ searchInstruction | translate }}
</ion-label>
</div>
<stapps-data-list

View File

@@ -37,7 +37,7 @@ ion-toolbar:first-of-type {
}
ion-content {
--background: var(--ion-color-light);
--background: var(--ion-background-color);
}
.content > div {

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Component, Input} from '@angular/core';
import {SCThings} from '@openstapps/core';
@Component({
<<<<<<<< HEAD:frontend/app/src/app/modules/data/list/data-list-item-host-default.component.ts
selector: 'data-list-item-host-default',
templateUrl: 'data-list-item-host-default.html',
})
export class DataListItemHostDefaultComponent {
@Input() item: SCThings;
}
========
selector: 'skeleton-list',
templateUrl: 'skeleton-list.html',
styleUrls: ['skeleton-list.scss'],
})
export class SkeletonListComponent {}
>>>>>>>> app/develop:frontend/app/src/app/modules/data/list/skeleton-list.component.ts

View File

@@ -0,0 +1,43 @@
<!--
~ Copyright (C) 2023 StApps
~ This program is free software: you can redistribute it and/or modify it
~ under the terms of the GNU General Public License as published by the Free
~ Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful, but WITHOUT
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
~ more details.
~
~ You should have received a copy of the GNU General Public License along with
~ this program. If not, see <https://www.gnu.org/licenses/>.
-->
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="list-item" width="100%" height="75" patternUnits="userSpaceOnUse">
<rect width="100%" height="67" class="item"></rect>
<mask id="label-mask">
<rect rx="8" x="24" y="24" width="36" height="36" fill="white"></rect>
<rect rx="6" x="72" y="24" width="180" height="12" fill="white"></rect>
<rect rx="6" x="72" y="48" width="92" height="12" fill="white"></rect>
</mask>
<g mask="url(#label-mask)">
<rect class="label" width="100%" height="100%"></rect>
<rect fill="white" width="16" height="100%" style="filter: blur(8px)">
<animateTransform
attributeName="transform"
attributeType="XML"
type="translate"
from="0 0"
to="1000 0"
dur="2s"
repeatCount="indefinite"
></animateTransform>
</rect>
</g>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#list-item)"></rect>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,34 @@
/*!
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
svg {
width: 100%;
height: 100%;
}
$item-height: 92;
$gap: 4;
.label {
fill: var(--ion-color-medium);
opacity: 0.1;
}
.item {
rx: var(--border-radius-default);
fill: var(--ion-item-background, var(--ion-background-color, #fff));
x: var(--spacing-sm);
y: var(--spacing-sm);
width: calc(100% - var(--spacing-sm) * 2);
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Component, Input} from '@angular/core';
import {SCDish} from '@openstapps/core';
@Component({
selector: 'stapps-dish-characteristics',
templateUrl: 'dish-characteristics.html',
styleUrls: ['dish-characteristics.scss'],
})
export class DishCharacteristicsComponent {
@Input() item: SCDish;
}

View File

@@ -1,5 +1,5 @@
<!--
~ Copyright (C) 2022 StApps
~ Copyright (C) 2023 StApps
~ This program is free software: you can redistribute it and/or modify it
~ under the terms of the GNU General Public License as published by the Free
~ Software Foundation, version 3.
@@ -12,22 +12,17 @@
~ 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-toolbar color="primary" mode="ios">
<ion-title>{{ 'schedule.addEventModal.addEvent' | translate | titlecase }}</ion-title>
<ion-buttons slot="end">
<ion-button fill="clear" (click)="modalController.dismiss()">
<ion-label>{{ 'modal.DISMISS' | translate }}</ion-label>
</ion-button>
</ion-buttons>
</ion-toolbar>
<ion-card-content>
<stapps-search-page
[showDrawer]="false"
[forcedFilter]="filter"
[itemRouting]="false"
[showTopToolbar]="false"
[showNavigation]="false"
></stapps-search-page>
</ion-card-content>
<ion-note>
<ng-container *ngIf="item.characteristics">
<ng-container *ngFor="let characteristic of 'characteristics' | thingTranslate: item">
<!-- Abbr tag shows the actual name on hover -->
<abbr
[style.--background-url]="'url(' + characteristic.image + ')'"
[title]="characteristic.name | titlecase"
></abbr>
</ng-container>
</ng-container>
<ion-label>
{{ 'categories' | thingTranslate: item | join: ', ' | titlecase }}
</ion-label>
</ion-note>

View File

@@ -0,0 +1,38 @@
/*!
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
ion-note {
list-style: none;
display: flex;
flex-direction: row-reverse;
justify-content: start;
}
abbr {
width: 16px;
aspect-ratio: 1;
background: var(--ion-color-medium);
mask-image: var(--background-url);
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
mask-mode: alpha;
+ ion-label::after {
content: '';
margin-inline: var(--spacing-xs);
}
}

View File

@@ -13,31 +13,7 @@
~ this program. If not, see <https://www.gnu.org/licenses/>.
-->
<ion-grid>
<ion-row>
<ion-col>
<ion-card *ngIf="item.categories">
<ion-card-header>
{{ 'categories' | thingTranslate: item | join: ', ' | titlecase }}
</ion-card-header>
</ion-card>
</ion-col>
<ion-col>
<ion-card *ngIf="item.characteristics">
<ion-card-header class="no-padding-inline-start vertical-list">
<ul>
<li>
<ng-container *ngFor="let characteristic of 'characteristics' | thingTranslate: item">
<img [src]="characteristic.image" [alt]="characteristic.name | titlecase" />
</ng-container>
</li>
</ul>
</ion-card-header>
</ion-card>
</ion-col>
</ion-row>
</ion-grid>
<stapps-dish-characteristics *ngIf="item.characteristics" [item]="item"></stapps-dish-characteristics>
<stapps-offers-detail *ngIf="item.offers" [offers]="item.offers"></stapps-offers-detail>
<!-- unwanted by swffm
<ion-card *ngIf="item.nutrition">
@@ -92,7 +68,7 @@
-->
<stapps-simple-card
*ngIf="item.additives"
[title]="'additives' | propertyNameTranslate: item"
[title]="$any('additives' | propertyNameTranslate: item) | titlecase"
[content]="'additives' | thingTranslate: item | join: ', '"
>
</stapps-simple-card>

View File

@@ -12,10 +12,7 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
@import 'src/theme/common/_helper.scss';
.vertical-list {
ul li img {
filter: unset;
}
stapps-dish-characteristics {
margin: var(--spacing-lg);
margin-block-end: var(--spacing-sm);
}

View File

@@ -14,28 +14,17 @@
-->
<ion-grid>
<ion-row>
<ion-col>
<ion-row class="ion-justify-content-between">
<ion-col size="11" size-sm="10">
<div class="ion-text-wrap">
<ion-label class="title">{{ 'name' | thingTranslate: item }}</ion-label>
<p class="title-sub ion-hide-sm-down">
{{ 'description' | thingTranslate: item }}
</p>
<ion-note>
<ul>
<li>
{{ 'categories' | thingTranslate: item | join: ', ' | titlecase }}
</li>
<li *ngIf="item.characteristics">
<ng-container *ngFor="let characteristic of 'characteristics' | thingTranslate: item">
<img [src]="characteristic.image" [alt]="characteristic.name | titlecase" />
</ng-container>
</li>
</ul>
</ion-note>
<stapps-dish-characteristics [item]="item"></stapps-dish-characteristics>
</div>
</ion-col>
<ion-col width-10 text-right>
<ion-col>
<div class="ion-text-end">
<stapps-offers-in-list *ngIf="item.offers" [offers]="item.offers"></stapps-offers-in-list>
</div>

View File

@@ -24,9 +24,9 @@
<p *ngIf="item.academicTerms" class="title-sub">
{{ 'name' | thingTranslate: item.academicTerms[0] }}
</p>
<ion-note *ngIf="!item.categories">{{ 'type' | thingTranslate: item }}</ion-note>
<ion-note *ngIf="!item.categories">{{ 'type' | thingTranslate: item | titlecase }}</ion-note>
<ion-note *ngIf="item.categories">
{{ 'categories' | thingTranslate: item | join: ', ' }}
{{ 'categories' | thingTranslate: item | join: ', ' | titlecase }}
</ion-note>
</div>
</ion-col>

View File

@@ -1,5 +1,5 @@
<!--
~ Copyright (C) 2022 StApps
~ Copyright (C) 2023 StApps
~ This program is free software: you can redistribute it and/or modify it
~ under the terms of the GNU General Public License as published by the Free
~ Software Foundation, version 3.
@@ -40,9 +40,8 @@
</div>
<ng-template #news>
<ion-thumbnail>
<ion-thumbnail *ngIf="item.image" style="background-image: url('{{ item.image }}')">
<ion-img
*ngIf="item.image"
src="{{ item.image }}"
(ionError)="$event.target.nextSibling.style.display = 'none'"
alt="{{ item.name }}"

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 StApps
/*!
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -15,17 +15,24 @@
:host {
ion-thumbnail {
position: relative;
width: 100%;
height: auto;
img {
display: block;
}
}
// Show smaller image on a desktop
@media (min-width: 992px) {
ion-thumbnail {
width: 60%;
margin: 0 auto;
margin-inline: auto;
overflow: hidden;
background-size: contain;
background-position: center;
transform: scaleX(-1);
ion-img {
backdrop-filter: blur(32px);
&::part(image) {
transform: scaleX(-1);
max-width: 16cm;
object-fit: contain;
margin-inline: auto;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 StApps
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -12,10 +12,13 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Component, Input, OnInit} from '@angular/core';
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
import {SCBuilding, SCFloor, SCPointOfInterest, SCRoom, SCThings} from '@openstapps/core';
import {DataProvider} from '../../data.provider';
import {hasValidLocation, isSCFloor} from './place-types';
import {DataRoutingService} from '../../data-routing.service';
import {Router} from '@angular/router';
import {Subscription} from 'rxjs';
/**
* TODO
@@ -26,7 +29,7 @@ import {hasValidLocation, isSCFloor} from './place-types';
selector: 'stapps-place-detail-content',
templateUrl: 'place-detail-content.html',
})
export class PlaceDetailContentComponent implements OnInit {
export class PlaceDetailContentComponent implements OnInit, OnDestroy {
/**
* TODO
*/
@@ -39,6 +42,8 @@ export class PlaceDetailContentComponent implements OnInit {
*/
hasValidLocation = false;
itemRouting: Subscription;
/**
* TODO
*
@@ -63,7 +68,17 @@ export class PlaceDetailContentComponent implements OnInit {
);
}
constructor(dataRoutingService: DataRoutingService, router: Router) {
this.itemRouting = dataRoutingService.itemSelectListener().subscribe(item => {
void router.navigate(['/data-detail', item.uid]);
});
}
ngOnInit() {
this.hasValidLocation = !isSCFloor(this.item) && hasValidLocation(this.item);
}
ngOnDestroy() {
this.itemRouting.unsubscribe();
}
}

View File

@@ -35,8 +35,7 @@
{{ 'inPlace' | propertyNameTranslate: item | titlecase }}
</ion-card-header>
<ion-card-content>
<ion-icon name="pin_drop"></ion-icon>
<a [routerLink]="['/data-detail', item.inPlace.uid]">{{ 'name' | thingTranslate: item.inPlace }}</a>
<stapps-data-list-item [item]="item.inPlace"></stapps-data-list-item>
</ion-card-content>
</ion-card>
</ng-container>

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 StApps
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -14,7 +14,7 @@
*/
import {Component, Input} from '@angular/core';
import {PositionService} from '../../../map/position.service';
import {Subscription, interval} from 'rxjs';
import {interval, Subscription} from 'rxjs';
import {hasValidLocation, isSCFloor, PlaceTypes, PlaceTypesWithDistance} from './place-types';
/**
@@ -23,6 +23,7 @@ import {hasValidLocation, isSCFloor, PlaceTypes, PlaceTypesWithDistance} from '.
@Component({
selector: 'stapps-place-list-item',
templateUrl: 'place-list-item.html',
styleUrls: ['place-list-item.scss'],
})
export class PlaceListItemComponent {
/**

View File

@@ -26,28 +26,24 @@
</p>
<p>
<ion-note *ngIf="item.categories && item.type !== 'building'; else onlyType">
<ul>
<li>
{{ 'categories' | thingTranslate: item | join: ', ' | titlecase }}
</li>
<li *ngIf="distance">
<ion-icon name="directions_walk"></ion-icon>
{{ distance | metersLocalized }}
</li>
</ul>
<ion-label>
{{ 'categories' | thingTranslate: item | join: ', ' | titlecase }}
</ion-label>
<ion-label *ngIf="distance" class="distance">
<ion-icon name="directions_walk"></ion-icon>
{{ distance | metersLocalized }}
</ion-label>
</ion-note>
</p>
<ng-template #onlyType>
<ion-note>
<ul>
<li>
{{ 'type' | thingTranslate: item }}
</li>
<li *ngIf="distance">
<ion-icon name="directions_walk"></ion-icon>
{{ distance | metersLocalized }}
</li>
</ul>
<ion-label>
{{ 'type' | thingTranslate: item | titlecase }}
</ion-label>
<ion-label *ngIf="distance" class="distance">
<ion-icon name="directions_walk"></ion-icon>
{{ distance | metersLocalized }}
</ion-label>
</ion-note>
</ng-template>
</ng-container>
@@ -56,10 +52,11 @@
</p>
</div>
</ion-col>
<div *ngIf="item.type !== 'building'">
<ion-col width-20 text-right *ngIf="item.inPlace">
<ion-icon name="pin_drop"></ion-icon>{{ 'name' | thingTranslate: item.inPlace }}
<ng-container *ngIf="item.type !== 'building'">
<ion-col size="auto" class="in-place" *ngIf="item.inPlace">
<ion-icon name="pin_drop"></ion-icon
><ion-label>{{ 'name' | thingTranslate: item.inPlace }}</ion-label>
</ion-col>
</div>
</ng-container>
</ion-row>
</ion-grid>

View File

@@ -0,0 +1,36 @@
/*!
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
ion-note {
display: flex;
> ion-label {
display: inline-flex;
justify-content: center;
align-items: center;
}
}
ion-label + ion-label.distance::before {
content: '';
margin-inline: var(--spacing-xs);
}
.in-place {
display: flex;
justify-content: end;
align-items: center;
}

View File

@@ -51,6 +51,8 @@ export class FavoritesService {
// using debounce time 0 allows change detection to run through async suspension
favoritesChanged$ = this.favorites.pipe(debounceTime(0));
favoriteThings$ = this.favoritesChanged$.pipe(map(favorite => [...favorite.values()].map(it => it.data)));
static getDataFromFavorites(items: SCFavorite[]) {
return items.map(item => item.data);
}

View File

@@ -21,107 +21,105 @@
<ion-title>{{ 'feedback.page.TITLE' | translate }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-content-parallax">
<div>
<div class="feedback-content">
<ion-card>
<form #feedbackForm="ngForm" (ngSubmit)="onSubmit()">
<ion-item>
<ion-label position="stacked">{{ 'feedback.form.name.label' | translate }}</ion-label>
<ion-input
placeholder="{{ 'feedback.form.name.placeholder' | translate }}"
[(ngModel)]="author.name"
name="name"
></ion-input>
</ion-item>
<ion-item>
<ion-label position="stacked">{{ 'feedback.form.type.label' | translate }}</ion-label>
<ion-select
[(ngModel)]="message.name"
value="comment"
name="title"
interface="popover"
required="true"
>
<ion-select-option value="Comment">{{
'feedback.form.type.values.comment' | translate
}}</ion-select-option>
<ion-select-option value="Bug">{{
'feedback.form.type.values.bug' | translate
}}</ion-select-option>
</ion-select>
</ion-item>
<ion-item>
<ion-label position="stacked">{{ 'feedback.form.email.label' | translate }}</ion-label>
<ion-input
placeholder="{{ 'feedback.form.email.placeholder' | translate }}"
[(ngModel)]="author.email"
type="email"
name="email"
ngModel
email
></ion-input>
</ion-item>
<ion-item>
<ion-label position="stacked">{{ 'feedback.form.message.label' | translate }}</ion-label>
<ion-textarea
[(ngModel)]="message.messageBody"
placeholder="{{
'feedback.form.message.placeholder' | translate: {number: MINIMUM_MESSAGE_SIZE}
}}"
name="message"
required="true"
minlength="{{ MINIMUM_MESSAGE_SIZE }}"
autoGrow="true"
></ion-textarea>
</ion-item>
<ion-item>
<ion-label class="ion-text-wrap">{{ 'feedback.form.termsAgree.0' | translate }} </ion-label>
<ion-checkbox
color="primary"
slot="start"
[(ngModel)]="termsAgree"
name="termsAgree"
></ion-checkbox>
</ion-item>
<ion-item lines="none">
<ion-label
><a style="display: contents" [routerLink]="['/about/privacy']">{{
'feedback.form.termsAgree.1' | translate
}}</a></ion-label
>
</ion-item>
<ion-item>
<ion-label class="ion-text-wrap">{{ 'feedback.form.protocolDataAgree' | translate }}</ion-label>
<ion-checkbox
color="primary"
slot="start"
[(ngModel)]="protocolDataAgree"
name="protocolDataAgree"
></ion-checkbox>
</ion-item>
<ion-card>
<ion-card-title>
<ion-button expand="block" fill="clear" (click)="toggleShowMetaData()">
<ng-container *ngIf="!showMetaData; else hide">{{
'feedback.form.protocolData.show' | translate
}}</ng-container>
<ng-template #hide>{{ 'feedback.form.protocolData.hide' | translate }}</ng-template>
</ion-button>
</ion-card-title>
<ion-card-content *ngIf="metaData && showMetaData">
<pre>{{ metaData | json }}</pre>
</ion-card-content>
</ion-card>
<ion-button
type="submit"
color="primary"
expand="block"
[disabled]="!feedbackForm.valid || !termsAgree || submitSuccess"
>{{ 'feedback.form.submit' | translate }}</ion-button
<ion-content parallax>
<div class="feedback-content">
<ion-card>
<form #feedbackForm="ngForm" (ngSubmit)="onSubmit()">
<ion-item>
<ion-label position="stacked">{{ 'feedback.form.name.label' | translate }}</ion-label>
<ion-input
placeholder="{{ 'feedback.form.name.placeholder' | translate }}"
[(ngModel)]="author.name"
name="name"
></ion-input>
</ion-item>
<ion-item>
<ion-label position="stacked">{{ 'feedback.form.type.label' | translate }}</ion-label>
<ion-select
[(ngModel)]="message.name"
value="comment"
name="title"
interface="popover"
required="true"
>
</form>
</ion-card>
</div>
<ion-select-option value="Comment">{{
'feedback.form.type.values.comment' | translate
}}</ion-select-option>
<ion-select-option value="Bug">{{
'feedback.form.type.values.bug' | translate
}}</ion-select-option>
</ion-select>
</ion-item>
<ion-item>
<ion-label position="stacked">{{ 'feedback.form.email.label' | translate }}</ion-label>
<ion-input
placeholder="{{ 'feedback.form.email.placeholder' | translate }}"
[(ngModel)]="author.email"
type="email"
name="email"
ngModel
email
></ion-input>
</ion-item>
<ion-item>
<ion-label position="stacked">{{ 'feedback.form.message.label' | translate }}</ion-label>
<ion-textarea
[(ngModel)]="message.messageBody"
placeholder="{{
'feedback.form.message.placeholder' | translate: {number: MINIMUM_MESSAGE_SIZE}
}}"
name="message"
required="true"
minlength="{{ MINIMUM_MESSAGE_SIZE }}"
autoGrow="true"
></ion-textarea>
</ion-item>
<ion-item>
<ion-label class="ion-text-wrap">{{ 'feedback.form.termsAgree.0' | translate }} </ion-label>
<ion-checkbox
color="primary"
slot="start"
[(ngModel)]="termsAgree"
name="termsAgree"
></ion-checkbox>
</ion-item>
<ion-item lines="none">
<ion-label
><a style="display: contents" [routerLink]="['/about/privacy']">{{
'feedback.form.termsAgree.1' | translate
}}</a></ion-label
>
</ion-item>
<ion-item>
<ion-label class="ion-text-wrap">{{ 'feedback.form.protocolDataAgree' | translate }}</ion-label>
<ion-checkbox
color="primary"
slot="start"
[(ngModel)]="protocolDataAgree"
name="protocolDataAgree"
></ion-checkbox>
</ion-item>
<ion-card>
<ion-card-title>
<ion-button expand="block" fill="clear" (click)="toggleShowMetaData()">
<ng-container *ngIf="!showMetaData; else hide">{{
'feedback.form.protocolData.show' | translate
}}</ng-container>
<ng-template #hide>{{ 'feedback.form.protocolData.hide' | translate }}</ng-template>
</ion-button>
</ion-card-title>
<ion-card-content *ngIf="metaData && showMetaData">
<pre>{{ metaData | json }}</pre>
</ion-card-content>
</ion-card>
<ion-button
type="submit"
color="primary"
expand="block"
[disabled]="!feedbackForm.valid || !termsAgree || submitSuccess"
>{{ 'feedback.form.submit' | translate }}</ion-button
>
</form>
</ion-card>
</div>
</ion-content>

View File

@@ -57,7 +57,7 @@ ion-button {
display: flex;
flex-direction: column;
gap: var(--spacing-sm);
background: var(--ion-color-light);
background: var(--ion-item-background);
@include border-radius-in-parallax(var(--border-radius-default));
& > * {
@@ -70,7 +70,6 @@ ion-button {
@include border-radius-in-parallax(var(--border-radius-default));
overflow: hidden;
position: relative;
background-color: var(--ion-color-primary-contrast);
margin: 0;
& > ion-thumbnail {

View File

@@ -1,5 +1,5 @@
/*!
* Copyright (C) 2022 StApps
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -87,18 +87,10 @@ div.map-buttons {
ion-button {
margin: 4px;
// important for iOS
// TODO: find an option that is better suited for the iOS theme
--box-shadow: var(--map-box-shadow);
align-self: flex-end;
}
ion-button::part(native) {
background: white;
}
ion-button::part(native):hover,
ion-button::part(native):focus {
background: whitesmoke;
}
}
div.map-buttons.above {

View File

@@ -1,3 +1,18 @@
/*!
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
div.map-container {
height: 100%;
width: 100%;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 StApps
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -159,6 +159,11 @@ describe('ContextMenuComponent', async () => {
{
field: 'type',
buckets: [{count: 10, key: 'date series', checked: true}],
info: {
onlyOnType: SCThingType.AcademicEvent,
field: 'date series',
sortOrder: 0,
},
},
];
@@ -210,7 +215,7 @@ function getFilterContextType(): FilterContext {
compact: false,
options: facetsMock
.filter(facet => facet.buckets.length > 0)
.map(facet => {
.map((facet, i) => {
return {
buckets: facet.buckets.map(bucket => {
return {
@@ -222,6 +227,11 @@ function getFilterContextType(): FilterContext {
compact: false,
field: facet.field,
onlyOnType: facet.onlyOnType,
info: {
onlyOnType: facet.onlyOnType,
field: facet.field,
sortOrder: i,
},
};
}),
};

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 StApps
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -17,7 +17,7 @@ import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
import {SCLanguage, SCThingTranslator, SCThingType, SCTranslations} from '@openstapps/core';
import {Subscription} from 'rxjs';
import {ContextMenuService} from './context-menu.service';
import {FilterContext, SortContext, SortContextOption} from './context-type';
import {FilterContext, FilterFacet, SortContext, SortContextOption} from './context-type';
/**
* The context menu
@@ -49,6 +49,19 @@ export class ContextMenuComponent implements OnDestroy {
*/
filterOption: FilterContext;
/**
* Picks facets based on the compact filter option and sorts
* them based on
*
* No specific type => Type name alphabetically => Bucket count
*/
get facets(): FilterFacet[] {
const options = this.filterOption.compact
? this.filterOption.options.slice(0, this.compactFilterOptionCount)
: this.filterOption.options;
return options.filter(it => it.buckets.length > 0);
}
/**
* Possible languages to be used for translation
*/
@@ -102,18 +115,6 @@ export class ContextMenuComponent implements OnDestroy {
this.contextMenuService.contextFilterChanged(this.filterOption);
};
/**
* Returns translated property name
*/
getTranslatedPropertyName(property: string, onlyForType?: SCThingType): string {
return (
this.translator.translatedPropertyNames(
onlyForType ?? SCThingType.AcademicEvent,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) as any
)[property];
}
/**
* Returns translated property value
*/

View File

@@ -1,5 +1,5 @@
<!--
~ Copyright (C) 2022 StApps
~ Copyright (C) 2023 StApps
~ This program is free software: you can redistribute it and/or modify it
~ under the terms of the GNU General Public License as published by the Free
~ Software Foundation, version 3.
@@ -41,7 +41,7 @@
<ion-icon *ngIf="!sortOption.reversed" name="arrow_upward"></ion-icon>
</span>
</ion-label>
<ion-radio slot="end" [value]="i"> </ion-radio>
<ion-radio slot="end" [value]="i"></ion-radio>
</ion-item>
</ion-radio-group>
</ion-list>
@@ -55,31 +55,17 @@
</ion-button>
</ion-list-header>
<ion-list
class="filter-group"
*ngFor="
let facet of !filterOption.compact
? filterOption.options.slice(0, compactFilterOptionCount)
: filterOption.options
"
>
<div *ngIf="!facet.field.includes('.')">
<ion-list class="filter-group" *ngFor="let facet of facets">
<div>
<ion-list-header class="h3">
<ion-label>
{{
(facet.onlyOnType
? getTranslatedPropertyName(facet.field, facet.onlyOnType)
: getTranslatedPropertyName(facet.field)
) | titlecase
}}
{{
facet.onlyOnType
? ' | ' + (getTranslatedPropertyValue(facet.onlyOnType, 'type') | titlecase)
: ''
}}
<span *ngIf="facet.info.onlyOnType"
><b>{{ facet.info.onlyOnType | titlecase }}</b> /
</span>
{{ facet.info.field | titlecase }}
</ion-label>
</ion-list-header>
<div *ngIf="facet.buckets.length > 0">
<div>
<ion-item
*ngFor="
let bucket of !facet.compact

View File

@@ -1,14 +1,32 @@
/*
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {TestBed} from '@angular/core/testing';
import {ContextMenuService} from './context-menu.service';
import {SCFacet} from '@openstapps/core';
import {FilterContext, SortContext} from './context-type';
import {ThingTranslateModule} from '../../../translation/thing-translate.module';
import {TranslateModule} from '@ngx-translate/core';
describe('ContextMenuService', () => {
let service: ContextMenuService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ThingTranslateModule.forRoot(), TranslateModule.forRoot()],
providers: [ContextMenuService],
});
service = TestBed.inject(ContextMenuService);
@@ -123,6 +141,10 @@ const filterContext: FilterContext = {
},
],
field: 'type',
info: {
field: 'type',
sortOrder: 0,
},
},
],
};

View File

@@ -13,9 +13,19 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Injectable} from '@angular/core';
import {SCFacet, SCSearchFilter, SCSearchSort, SCThingType} from '@openstapps/core';
import {
SCFacet,
SCSearchFilter,
SCSearchSort,
SCThingTranslator,
SCThingType,
SCTranslations,
} from '@openstapps/core';
import {Subject} from 'rxjs';
import {FilterBucket, FilterContext, FilterFacet, SortContext} from './context-type';
import {FilterBucket, FilterContext, FilterFacet, SortContext, TransformedFacet} from './context-type';
import {TranslateService} from '@ngx-translate/core';
import {ThingTranslateService} from '../../../translation/thing-translate.service';
import {transformFacets} from './facet-filter';
/**
* ContextMenuService provides bidirectional communication of context menu options and search queries
@@ -72,6 +82,11 @@ export class ContextMenuService {
*/
sortQueryChanged$ = this.sortQuery.asObservable();
constructor(
private readonly translate: TranslateService,
private readonly thingTranslate: ThingTranslateService,
) {}
/**
* Returns SCSearchFilter if filterContext value is set, undefined otherwise
*
@@ -178,18 +193,9 @@ export class ContextMenuService {
* Updates the filter context options from given facets
*/
updateContextFilter(facets: SCFacet[]) {
// arrange facet field "type" to first position
facets.sort((a: SCFacet, b: SCFacet) => {
if (a.field === 'type') {
return -1;
}
if (b.field === 'type') {
return 1;
}
return 0;
});
const language = this.translate.currentLang as keyof SCTranslations<unknown>;
const translator = new SCThingTranslator(language);
const transformedFacets = transformFacets(facets, language, this.thingTranslate, translator);
if (!this.contextFilter) {
this.contextFilter = {
@@ -198,23 +204,24 @@ export class ContextMenuService {
};
}
this.updateContextFilterOptions(this.contextFilter, facets);
this.updateContextFilterOptions(this.contextFilter, transformedFacets);
}
/**
* Updates context filter with new facets.
* It preserves the checked status of existing filter options
*/
updateContextFilterOptions = (contextFilter: FilterContext, facets: SCFacet[]) => {
updateContextFilterOptions = (contextFilter: FilterContext, facets: TransformedFacet[]) => {
const newFilterOptions: FilterFacet[] = [];
// iterate new facets
for (const facet of facets) {
for (const {facet, info} of facets) {
if (facet.buckets.length > 0) {
const newFilterFacet: FilterFacet = {
buckets: [],
field: facet.field,
onlyOnType: facet.onlyOnType,
info,
};
newFilterOptions.push(newFilterFacet);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 StApps
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -12,7 +12,7 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {SCFacet, SCFacetBucket} from '@openstapps/core';
import {SCFacet, SCFacetBucket, SCThingType} from '@openstapps/core';
export type ContextType = FilterContext | SortContext;
@@ -84,6 +84,21 @@ export interface FilterFacet extends SCFacet {
* Compact view of the option buckets
*/
compact?: boolean;
/**
* Translated information about the facet
*/
info: FacetInfo;
}
export interface FacetInfo {
onlyOnType?: SCThingType;
field: string;
sortOrder: number;
}
export interface TransformedFacet {
facet: SCFacet;
info: FacetInfo;
}
export interface FilterBucket extends SCFacetBucket {

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {TransformedFacet} from './context-type';
import {SCFacet, SCThingTranslator, SCThingType, SCTranslations} from '@openstapps/core';
import {searchFilters} from '../../../../config/search-filter';
import {ThingTranslateService} from '../../../translation/thing-translate.service';
const filterConfig = Object.entries(searchFilters).map(([pattern, entries]) => {
return {
typePattern: new RegExp(`^${pattern}$`),
facetFilter: Object.entries(entries).map(([pattern, facet]) => ({
pattern: new RegExp(`^${pattern}$`),
...facet,
})),
};
});
/**
* Transforms facets to
*
* 1. only include facets that are allowed in the options
* 2. translates all fields
* 3. sorts the facets according to the config
*/
export function transformFacets(
facets: SCFacet[],
language: keyof SCTranslations<unknown>,
thingTranslate: ThingTranslateService,
translator: SCThingTranslator,
): TransformedFacet[] {
return facets
.map(facet => ({
facet,
info: filterConfig
.filter(({typePattern}) => typePattern.test((facet.onlyOnType as string) || ''))
.flatMap(({facetFilter}) =>
facetFilter
.filter(({pattern}) => pattern.test(facet.field))
.map(it => ({
onlyOnType: facet.onlyOnType
? (translator.translatedPropertyValue(facet.onlyOnType, 'type') as SCThingType)
: undefined,
field:
it.translations && it.name
? it.translations[language]?.name || it.name
: thingTranslate.getPropertyName(
facet.onlyOnType || SCThingType.AcademicEvent,
facet.field,
),
sortOrder: it.sortOrder,
})),
)
.sort(({sortOrder: a}, {sortOrder: b}) => a - b)[0],
}))
.filter(({info}) => !!info)
.sort(({info: {sortOrder: a}}, {info: {sortOrder: b}}) => a - b);
}

View File

@@ -14,7 +14,7 @@
-->
<stapps-offline-notice></stapps-offline-notice>
<ion-split-pane contentId="main" when="lg">
<ion-split-pane contentId="main" when="xl">
<ion-menu menuId="main" contentId="main" type="overlay" side="start" swipe-gesture="false">
<ion-header>
<ion-toolbar color="primary" mode="ios">

View File

@@ -16,7 +16,7 @@
@import '../../../../theme/util/mixins';
stapps-navigation-tabs {
@include ion-lg-up {
@include ion-xl-up {
display: none;
}
}
@@ -41,7 +41,7 @@ stapps-offline-notice.is-offline ~ ion-split-pane {
margin-left: var(--navigation-rail-width);
}
@include ion-lg-up {
@include ion-xl-up {
margin-left: 0;
}
}
@@ -66,7 +66,7 @@ stapps-offline-notice.is-offline ~ ion-split-pane {
}
ion-router-outlet {
background: white;
background: var(--ion-background-color);
}
.menu-category {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 StApps
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -12,7 +12,6 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Directive, ElementRef, Input, OnDestroy, OnInit, Renderer2} from '@angular/core';
import {AnimationController, NavController} from '@ionic/angular';
import {Router, RouterEvent} from '@angular/router';
@@ -46,6 +45,12 @@ export class RootLinkDirective implements OnInit, OnDestroy {
ngOnInit() {
const animation = tabsTransition(this.animationController);
this.renderer.setAttribute(this.element.nativeElement, 'button', '');
if (document.querySelector('#main')?.childNodes.length === 1) {
if (this.router.url === this.rootLink) {
this.setActive();
}
this.needsInit = false;
}
this.subscriptions.push(
this.router.events.subscribe(event => {

View File

@@ -23,7 +23,7 @@ ion-card::part(native) {
display: flex;
flex-direction: column-reverse;
height: 100%;
background: linear-gradient(to top, var(--ion-color-dark), transparent);
background: linear-gradient(to top, #0e0e0e, transparent);
}
.card {
@@ -38,10 +38,10 @@ ion-card-title {
-webkit-box-orient: vertical;
overflow: hidden;
font-size: var(--font-size-xl);
--color: var(--ion-color-dark-contrast);
--color: var(--ion-text-color-dark, white);
max-lines: 3;
}
ion-card-subtitle {
--color: var(--ion-color-dark-contrast);
--color: var(--ion-text-color-dark, white);
}

View File

@@ -22,41 +22,39 @@
</ion-toolbar>
</ion-header>
<ion-content class="ion-content-parallax" (elementSizeChange)="calcPageSize($event)">
<div>
<ion-refresher slot="fixed" (ionRefresh)="refresh($event.target)">
<ion-refresher-content
pullingIcon="chevron-down-outline"
pullingText="{{ 'data.REFRESH_ACTION' | translate }}"
refreshingText="{{ 'data.REFRESHING' | translate }}"
refreshingSpinner="crescent"
>
</ion-refresher-content>
</ion-refresher>
<ion-grid>
<ion-row>
<ion-col size="12">
<stapps-news-settings-filter
*ngIf="settings"
[settings]="settings"
(filtersChanged)="toggleFilter($event)"
></stapps-news-settings-filter>
</ion-col>
</ion-row>
</ion-grid>
<div class="news-grid">
<ng-container *ngIf="!news">
<stapps-skeleton-news-item *ngFor="let skeleton of [1, 2, 3, 4, 5]"></stapps-skeleton-news-item>
</ng-container>
<ng-container *ngIf="news.length > 0">
<stapps-news-item *ngFor="let item of news" [item]="item"></stapps-news-item>
</ng-container>
</div>
<ion-label *ngIf="news.length === 0" class="centeredMessageContainer">
{{ 'search.nothing_found' | translate | titlecase }}
</ion-label>
<ion-infinite-scroll id="infinite-scroll" threshold="20%" (ionInfinite)="loadMore($event.target)">
<ion-infinite-scroll-content loading-spinner="crescent"> </ion-infinite-scroll-content>
</ion-infinite-scroll>
<ion-content parallax (elementSizeChange)="calcPageSize($event)">
<ion-refresher slot="fixed" (ionRefresh)="refresh($event.target)">
<ion-refresher-content
pullingIcon="chevron-down-outline"
pullingText="{{ 'data.REFRESH_ACTION' | translate }}"
refreshingText="{{ 'data.REFRESHING' | translate }}"
refreshingSpinner="crescent"
>
</ion-refresher-content>
</ion-refresher>
<ion-grid>
<ion-row>
<ion-col size="12">
<stapps-news-settings-filter
*ngIf="settings"
[settings]="settings"
(filtersChanged)="toggleFilter($event)"
></stapps-news-settings-filter>
</ion-col>
</ion-row>
</ion-grid>
<div class="news-grid">
<ng-container *ngIf="!news">
<stapps-skeleton-news-item *ngFor="let skeleton of [1, 2, 3, 4, 5]"></stapps-skeleton-news-item>
</ng-container>
<ng-container *ngIf="news.length > 0">
<stapps-news-item *ngFor="let item of news" [item]="item"></stapps-news-item>
</ng-container>
</div>
<ion-label *ngIf="news.length === 0" class="centeredMessageContainer">
{{ 'search.nothing_found' | translate | titlecase }}
</ion-label>
<ion-infinite-scroll id="infinite-scroll" threshold="20%" (ionInfinite)="loadMore($event.target)">
<ion-infinite-scroll-content loading-spinner="crescent"> </ion-infinite-scroll-content>
</ion-infinite-scroll>
</ion-content>

View File

@@ -14,13 +14,11 @@
*/
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
import {SCSection} from './sections';
import {SCSection} from '../../../../config/profile-page-sections';
import {AuthHelperService} from '../../auth/auth-helper.service';
import {Observable, Subscription} from 'rxjs';
import {SCAuthorizationProviderType} from '@openstapps/core';
import Swiper from 'swiper';
import {AlertController} from '@ionic/angular';
import {TranslateService} from '@ngx-translate/core';
@Component({
selector: 'stapps-profile-page-section',
@@ -55,11 +53,7 @@ export class ProfilePageSectionComponent implements OnInit, OnDestroy {
},
};
constructor(
private authHelper: AuthHelperService,
private alertController: AlertController,
private translateService: TranslateService,
) {}
constructor(private authHelper: AuthHelperService) {}
ngOnInit() {
if (this.item.authProvider) {
@@ -100,27 +94,7 @@ export class ProfilePageSectionComponent implements OnInit, OnDestroy {
async signOut(providerType: SCAuthorizationProviderType) {
await this.authHelper.getProvider(providerType).signOut();
const alert: HTMLIonAlertElement = await this.alertController.create({
header: this.translateService.instant(`auth.messages.${providerType}.log_out_alert.header`),
message: this.translateService.instant(`auth.messages.${providerType}.log_out_alert.message`),
buttons: [
{
text: this.translateService.instant('no'),
cssClass: 'default',
},
{
text: this.translateService.instant('yes'),
role: 'confirm',
cssClass: 'preferred',
handler: () => {
this.authHelper.endBrowserSession(providerType);
},
},
],
});
await alert.present();
await this.authHelper.endBrowserSession(providerType);
}
ngOnDestroy() {

View File

@@ -28,6 +28,7 @@
*ngFor="let link of item.links"
[routerLink]="link.link"
[disabled]="link.needsAuth && !isLoggedIn"
[detail]="false"
>
<div>
<ion-icon [name]="link.icon" size="36" color="dark"></ion-icon>

View File

@@ -12,6 +12,8 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
@use 'sass:math';
$width: 108px;
simple-swiper {
@@ -22,7 +24,7 @@ simple-swiper {
@each $i in 7, 6, 5, 4, 3, 2, 1 {
$max: #{($width + 8px) * $i};
@container (inline-size < #{$max}) {
--swiper-slide-width: #{100cqi / $i};
--swiper-slide-width: #{math.div(100cqi, $i)};
}
}
}
@@ -50,6 +52,7 @@ ion-item {
justify-content: center;
align-items: center;
font-size: var(--font-size-sm);
padding: 0 var(--spacing-xs) 0 var(--spacing-xs);
}
&::part(native) {

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