diff --git a/README.md b/README.md index 5ec6a1fec..1acac5263 100755 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![BCH compliance](https://bettercodehub.com/edge/badge/RADAR-base/RADAR-Questionnaire?branch=master)](https://bettercodehub.com/) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/03ec17f46bf147278bc71242a769af88)](https://www.codacy.com/app/yatharthranjan89/RADAR-Questionnaire?utm_source=github.com&utm_medium=referral&utm_content=RADAR-base/RADAR-Questionnaire&utm_campaign=Badge_Grade) -Hybrid mobile application to actively capture data for the RADAR-Base Platform. +A hybrid mobile application to actively capture data for the RADAR-Base Platform. ## Note @@ -38,7 +38,7 @@ To run the application in the browser use: $ ionic serve ``` -## Fix CSS +## Guidelines Use the following command to sort, format and fix common css problems: @@ -46,9 +46,19 @@ Use the following command to sort, format and fix common css problems: $ yarn fix:css ``` +Use the following command before commiting to fix all common styling and sorting problems: + +``` +$ yarn fix:all +``` + +## Platforms + +In order to add platforms to target, you must install the required SDKs. + ### Android -To add the Android platform. You need to have the [Android SDK](https://developer.android.com/studio/index.html) pre installed. This step also adds the plugins listed in `config.xml` to the project. +To add the Android platform, you need to have the [Android SDK](https://developer.android.com/studio/index.html) pre installed. This step also adds the plugins listed in `config.xml` to the project. ``` $ ionic cordova platform add android @@ -66,7 +76,36 @@ Run the app in an Android emulator: $ ionic cordova emulate android ``` -## Firebase remote config +### iOS + +To add the iOS platform, you need to have [XCode](https://apps.apple.com/us/app/xcode/id497799835?mt=12) pre installed. + +``` +$ ionic cordova platform add ios +``` + +Run the app in an iOS device: + +``` +$ ionic cordova run ios +``` + +## Firebase + +If using Firebase for notifications, analytics, or remote config, [create your Firebase project](https://console.firebase.google.com/). Then, add your iOS or Android app to the Firebase project. Once added, please download the app's `google-service.json` file (for Android) and `GoogleService-Info.plist` (for iOS), and add it to the root directory. + +### Remote Notifications + +If using FCM pull notifications instead of the local ones, please specify the FCM sender id (as mentioned in FCM settings) in `src/assets/data/defaultConfig.ts` and the default notification type to FCM (this is already the default value). + +```ts +export const FCMPluginProjectSenderId = 'your-sender-id' +export const DefaultNotificationType = 'FCM' +``` + +In order for notifications to be sent, you must run your own app server. It is part of the [RADAR-base stack](https://github.com/RADAR-base/RADAR-Docker/), and specific docs can be found [here](https://github.com/RADAR-base/RADAR-Docker/tree/dev/dcompose-stack/firebase-app-server). + +### Remote Config Certain values can be overriden using Firebase Remote Config. Specifically, the following variables are supported: @@ -81,28 +120,36 @@ Certain values can be overriden using Firebase Remote Config. Specifically, the | `kafka_specification_url` | URL of the Kafka topic specification | | | `platform_instance` | Title of RADAR Base / platform instance | `RADAR-CNS` | +### Analytics + +In order to personalize Firebase events, certain user properties must be added to the Firebase console. Specifically, the following user properties are supported: + +| Property | Description | +| ----------------- | ----------------------------------------------------------------- | +| `subjectId` | Custom identifier for the user | +| `humanReadableId` | Human readable identifier for the user | +| `baseUrl` | Custom identifier for the base URL of the project | +| `projectId` | Custom identifier for the project that a user belongs to | +| `sourceId` | Custom identifier for the source the application is registered as | +| `enrolmentDate` | Enrolment date of the user | + +Further details on the events that are already logged, default events, and default user properties can be found on the [RADAR Base wiki pages](https://radar-base.atlassian.net/wiki/spaces/RAD/pages/905707521/Firebase+Analytics). + ## Other Config Options Copy `src/assets/data/secret.ts.template` to `src/assets/data/secret.ts` and add the following configuration - ```ts // The client secret for OAuth authorisation with the Management Portal -export const DefaultSourceProducerAndSecretExport: string = 'aRMT:' +export const DefaultSourceProducerAndSecretExport = 'aRMT:' ``` In `src/assets/data/defaultConfig.ts` the following settings can be changed: -If using FCM pull notifications instead of the local ones, please specify the FCM sender id (as mentioned in FCM settings) in `src/assets/data/defaultConfig.ts`. Don't forget to add the app's `google-services.json` (Android) or `GoogleService-Info.plist` (iOS) file to the root of your project. - -```ts -export const FCMPluginProjectSenderId: string = 'your-sender-id' -``` - The Default endpoint of where the RADAR-base platform is hosted. ```ts -export const DefaultEndPoint: string = - 'https://your-hosted-radar-platform-base-url/' +export const DefaultEndPoint = 'https://your-hosted-radar-platform-base-url/' ``` Also change the Default Github source details where the questionnaire scheduling protocols and questionnaire schemas are hosted. diff --git a/config.xml b/config.xml index 945ea98a0..66b0d535b 100755 --- a/config.xml +++ b/config.xml @@ -1,5 +1,5 @@ - + RADAR Questionnaire An application that collects active data for research. RADAR-Base @@ -12,6 +12,7 @@ + @@ -127,6 +128,7 @@ + diff --git a/package.json b/package.json index 89538c799..c046b7264 100644 --- a/package.json +++ b/package.json @@ -47,28 +47,28 @@ "@angular/forms": "^7.2.13", "@angular/router": "^7.2.13", "@auth0/angular-jwt": "^3.0.0", - "@ionic-native/android-permissions": "^5.14.0", - "@ionic-native/app-version": "^5.14.0", - "@ionic-native/background-mode": "^5.14.0", - "@ionic-native/barcode-scanner": "^5.14.0", - "@ionic-native/core": "^5.14.0", - "@ionic-native/device": "^5.14.0", - "@ionic-native/dialogs": "^5.14.0", - "@ionic-native/file": "^5.14.0", - "@ionic-native/firebase": "^5.14.0", - "@ionic-native/globalization": "^5.14.0", - "@ionic-native/insomnia": "^5.14.0", - "@ionic-native/keyboard": "^5.14.0", - "@ionic-native/local-notifications": "^5.14.0", - "@ionic-native/mobile-accessibility": "5.14.0", - "@ionic-native/splash-screen": "^5.14.0", - "@ionic-native/status-bar": "^5.14.0", - "@ionic-native/vibration": "^5.14.0", - "@ionic-native/web-intent": "^5.14.0", + "@ionic-native/android-permissions": "^5.15.0", + "@ionic-native/app-version": "^5.15.0", + "@ionic-native/background-mode": "^5.15.0", + "@ionic-native/barcode-scanner": "^5.15.0", + "@ionic-native/core": "^5.15.0", + "@ionic-native/device": "^5.15.0", + "@ionic-native/dialogs": "^5.15.0", + "@ionic-native/file": "^5.15.0", + "@ionic-native/firebase": "^5.15.0", + "@ionic-native/globalization": "^5.15.0", + "@ionic-native/insomnia": "^5.15.0", + "@ionic-native/keyboard": "^5.15.0", + "@ionic-native/local-notifications": "^5.15.0", + "@ionic-native/mobile-accessibility": "5.15.0", + "@ionic-native/splash-screen": "^5.15.0", + "@ionic-native/status-bar": "^5.15.0", + "@ionic-native/vibration": "^5.15.0", + "@ionic-native/web-intent": "^5.15.0", "@ionic/storage": "^2.2.0", "angular-svg-round-progressbar": "^3.0.1", - "autoprefixer": "^9.6.1", - "avsc": "^5.4.13", + "autoprefixer": "^9.6.4", + "avsc": "^5.4.14", "base64-js": "^1.3.1", "browserify": "^16.3.0", "com-darryncampbell-cordova-plugin-intent": "^1.1.4", @@ -92,7 +92,7 @@ "cordova-plugin-insomnia": "^4.3.0", "cordova-plugin-ionic-keyboard": "^2.2.0", "cordova-plugin-local-notification": "^0.9.0-beta.3", - "cordova-plugin-network-information": "git+https://github.com/apache/cordova-plugin-network-information.git", + "cordova-plugin-network-information": "^2.0.2", "cordova-plugin-splashscreen": "^5.0.3", "cordova-plugin-statusbar": "^2.4.3", "cordova-plugin-vibration": "^3.1.1", @@ -109,8 +109,9 @@ "phonegap-plugin-barcodescanner": "^8.1.0", "phonegap-plugin-mobile-accessibility": "^1.0.5", "rxjs": "^6.5.3", + "semver": "^6.3.0", "uuid": "^3.3.3", - "yaml": "^1.7.0", + "yaml": "^1.7.1", "zone.js": "~0.8.26" }, "devDependencies": { @@ -122,8 +123,8 @@ "@angular/platform-browser-dynamic": "^7.2.13", "@angular/platform-server": "^7.2.13", "@ionic/app-scripts": "^3.2.4", - "@types/jasmine": "^3.4.1", - "@types/jasminewd2": "^2.0.7", + "@types/jasmine": "^3.4.4", + "@types/jasminewd2": "^2.0.8", "@types/node": "^12.7.8", "codelyzer": "^5.1.2", "eslint": "^6.5.1", @@ -132,7 +133,7 @@ "import-sort-parser-babylon": "^6.0.0", "import-sort-parser-typescript": "^6.0.0", "import-sort-style-module": "^6.0.0", - "ionic": "^5.4.1", + "ionic": "^5.4.2", "jasmine": "^3.5.0", "jasmine-core": "^3.5.0", "jasmine-spec-reporter": "^4.2.1", @@ -160,7 +161,7 @@ "rxjs-compat": "^6.5.3", "rxjs-tslint": "0.1.5", "stylefmt": "^6.0.3", - "stylelint": "^11.0.0", + "stylelint": "^11.1.1", "stylelint-config-standard": "^19.0.0", "tslint": "^5.20.0", "tslint-angular": "^3.0.2", @@ -168,7 +169,7 @@ "tslint-config-standard": "^8.0.1", "typescript": "~3.1.6", "watchify": "^3.11.1", - "webpack": "^4.41.0", + "webpack": "^4.41.1", "webpack-sources": "^1.4.3" }, "cordova": { @@ -207,7 +208,8 @@ "cordova-plugin-androidx-adapter": {}, "cordova-plugin-background-mode-fixes": {}, "com-darryncampbell-cordova-plugin-intent": {}, - "cordova-plugin-ionic-keyboard": {} + "cordova-plugin-ionic-keyboard": {}, + "cordova-plugin-network-information": {} } }, "ionic_enable_lint": false diff --git a/src/app/core/services/config/config.service.spec.ts b/src/app/core/services/config/config.service.spec.ts index f32046db2..b603fda16 100644 --- a/src/app/core/services/config/config.service.spec.ts +++ b/src/app/core/services/config/config.service.spec.ts @@ -1,4 +1,7 @@ +import { HttpClient, HttpHandler } from '@angular/common/http' import { TestBed } from '@angular/core/testing' +import { Platform } from 'ionic-angular' +import { PlatformMock } from 'ionic-mocks' import { AppConfigServiceMock, @@ -17,12 +20,12 @@ import { LocalizationService } from '../misc/localization.service' import { LogService } from '../misc/log.service' import { NotificationService } from '../notifications/notification.service' import { ScheduleService } from '../schedule/schedule.service' +import { AnalyticsService } from '../usage/analytics.service' import { AppConfigService } from './app-config.service' import { ConfigService } from './config.service' import { ProtocolService } from './protocol.service' import { QuestionnaireService } from './questionnaire.service' import { SubjectConfigService } from './subject-config.service' -import { AnalyticsService } from '../usage/analytics.service'; describe('ConfigService', () => { let service @@ -43,7 +46,10 @@ describe('ConfigService', () => { provide: AnalyticsService, useClass: FirebaseAnalyticsServiceMock }, - { provide: LogService, useClass: LogServiceMock } + { provide: LogService, useClass: LogServiceMock }, + HttpClient, + HttpHandler, + { provide: Platform, useClass: PlatformMock } ] }) ) diff --git a/src/app/core/services/config/config.service.ts b/src/app/core/services/config/config.service.ts index e618a7bf9..118df4456 100755 --- a/src/app/core/services/config/config.service.ts +++ b/src/app/core/services/config/config.service.ts @@ -1,11 +1,22 @@ +import { HttpClient } from '@angular/common/http' import { Injectable } from '@angular/core' +import { Platform } from 'ionic-angular' +import * as ver from 'semver' -import { DefaultNotificationRefreshTime } from '../../../../assets/data/defaultConfig' +import { + DefaultAppId, + DefaultAppVersion, + DefaultAppleAppStoreAppURL, + DefaultGooglePlaystoreAppURL, + DefaultNotificationRefreshTime, + DefaultPackageName +} from '../../../../assets/data/defaultConfig' import { ConfigEventType, NotificationEventType } from '../../../shared/enums/events' import { User } from '../../../shared/models/user' +import { parseVersion } from '../../../shared/utilities/parse-version' import { TaskType } from '../../../shared/utilities/task-type' import { KafkaService } from '../kafka/kafka.service' import { LocalizationService } from '../misc/localization.service' @@ -30,7 +41,9 @@ export class ConfigService { private kafka: KafkaService, private localization: LocalizationService, private analytics: AnalyticsService, - private logger: LogService + private logger: LogService, + private http: HttpClient, + private platform: Platform ) {} fetchConfigState(force?: boolean) { @@ -45,6 +58,7 @@ export class ConfigService { this.subjectConfig .getEnrolmentDate() .then(d => this.appConfig.init(d)) + this.checkForAppUpdates() if (newProtocol) return this.updateConfigStateOnProtocolChange(newProtocol) if (newAppVersion) @@ -54,7 +68,7 @@ export class ConfigService { if (newNotifications) return this.rescheduleNotifications(false) }) .catch(e => { - this.sendConfigChangeEvent(ConfigEventType.ERROR) + this.sendConfigChangeEvent(ConfigEventType.ERROR, '', '', e.message) throw e }) } @@ -130,6 +144,24 @@ export class ConfigService { }) } + checkForAppUpdates() { + const playstoreURL = this.platform.is('ios') + ? DefaultAppleAppStoreAppURL + DefaultAppId + : DefaultGooglePlaystoreAppURL + DefaultPackageName + return Promise.all([ + this.http + .get(playstoreURL, { responseType: 'text' }) + .toPromise() + .then(res => parseVersion(res)) + .catch(e => DefaultAppVersion), + this.appConfig.getAppVersion() + ]).then(([playstoreVersion, currentVersion]) => { + if (ver.gt(playstoreVersion, currentVersion)) + throw new Error(ConfigEventType.APP_UPDATE_AVAILABLE) + return + }) + } + checkParticipantEnrolled() { return this.subjectConfig .getParticipantLogin() @@ -243,10 +275,11 @@ export class ConfigService { } } - sendConfigChangeEvent(type, previous?, current?) { + sendConfigChangeEvent(type, previous?, current?, error?) { this.analytics.logEvent(type, { previous: String(previous), - current: String(current) + current: String(current), + error: String(error) }) } diff --git a/src/app/core/services/notifications/notification-generator.service.ts b/src/app/core/services/notifications/notification-generator.service.ts index ab18f8318..c87b3e80e 100644 --- a/src/app/core/services/notifications/notification-generator.service.ts +++ b/src/app/core/services/notifications/notification-generator.service.ts @@ -2,7 +2,7 @@ import 'rxjs/add/operator/map' import { Injectable } from '@angular/core' -import { DefaultTaskTest } from '../../../../assets/data/defaultConfig' +import { DefaultTask } from '../../../../assets/data/defaultConfig' import { LocKeys } from '../../../shared/enums/localisations' import { Assessment } from '../../../shared/models/assessment' import { @@ -91,7 +91,7 @@ export class NotificationGeneratorService { createTestNotification() { return this.createNotification( - DefaultTaskTest, + DefaultTask, new Date().getTime() + getMilliseconds({ minutes: 2 }), NotificationType.TEST ) diff --git a/src/app/core/services/schedule/schedule-generator.service.spec.ts b/src/app/core/services/schedule/schedule-generator.service.spec.ts index 3acb17724..225d61fab 100644 --- a/src/app/core/services/schedule/schedule-generator.service.spec.ts +++ b/src/app/core/services/schedule/schedule-generator.service.spec.ts @@ -4,8 +4,10 @@ import { LocalizationServiceMock, LogServiceMock, NotificationGeneratorServiceMock, - QuestionnaireServiceMock + QuestionnaireServiceMock, + UtilityMock } from '../../../shared/testing/mock-services' +import { Utility } from '../../../shared/utilities/util' import { QuestionnaireService } from '../config/questionnaire.service' import { LocalizationService } from '../misc/localization.service' import { LogService } from '../misc/log.service' @@ -28,6 +30,10 @@ describe('ScheduleGeneratorService', () => { { provide: NotificationGeneratorService, useClass: NotificationGeneratorServiceMock + }, + { + provide: Utility, + useClass: UtilityMock } ] }) diff --git a/src/app/core/services/schedule/schedule-generator.service.ts b/src/app/core/services/schedule/schedule-generator.service.ts index ff181347c..ad6168303 100644 --- a/src/app/core/services/schedule/schedule-generator.service.ts +++ b/src/app/core/services/schedule/schedule-generator.service.ts @@ -3,6 +3,7 @@ import { Injectable } from '@angular/core' import { DefaultESMCompletionWindow, DefaultScheduleYearCoverage, + DefaultTask, DefaultTaskCompletionWindow } from '../../../../assets/data/defaultConfig' import { Assessment } from '../../../shared/models/assessment' @@ -15,6 +16,7 @@ import { setDateTimeToMidnight, timeIntervalToMillis } from '../../../shared/utilities/time' +import { Utility } from '../../../shared/utilities/util' import { QuestionnaireService } from '../config/questionnaire.service' import { LocalizationService } from '../misc/localization.service' import { LogService } from '../misc/log.service' @@ -26,7 +28,8 @@ export class ScheduleGeneratorService { private notificationService: NotificationGeneratorService, private localization: LocalizationService, private questionnaire: QuestionnaireService, - private logger: LogService + private logger: LogService, + private util: Utility ) {} runScheduler( @@ -158,21 +161,21 @@ export class ScheduleGeneratorService { timestamp: number, completionWindow ): Task { - const task: Task = { - index, - timestamp, - completed: false, - reportedCompletion: false, - name: assessment.name, - nQuestions: assessment.questions.length, - estimatedCompletionTime: assessment.estimatedCompletionTime, - completionWindow: completionWindow, - warning: this.localization.chooseText(assessment.warn), - isClinical: assessment.protocol.clinicalProtocol ? true : false, - showInCalendar: - assessment.showInCalendar == null ? true : assessment.showInCalendar, - isDemo: !!assessment.isDemo - } + const task: Task = this.util.deepCopy(DefaultTask) + task.index = index + task.timestamp = timestamp + task.name = assessment.name + task.nQuestions = assessment.questions.length + task.estimatedCompletionTime = assessment.estimatedCompletionTime + task.completionWindow = completionWindow + task.warning = this.localization.chooseText(assessment.warn) + task.isClinical = !!assessment.protocol.clinicalProtocol + task.showInCalendar = this.getOrDefault( + assessment.showInCalendar, + task.showInCalendar + ) + task.isDemo = this.getOrDefault(assessment.isDemo, task.isDemo) + task.order = this.getOrDefault(assessment.order, task.order) task.notifications = this.notificationService.createNotifications( assessment, task @@ -180,6 +183,11 @@ export class ScheduleGeneratorService { return task } + getOrDefault(val, defaultVal) { + if (val == null) return defaultVal + return val + } + updateScheduleWithCompletedTasks( schedule: Task[], completedTasks, diff --git a/src/app/core/services/usage/firebase-analytics.service.ts b/src/app/core/services/usage/firebase-analytics.service.ts index 428ace67e..756d4622b 100644 --- a/src/app/core/services/usage/firebase-analytics.service.ts +++ b/src/app/core/services/usage/firebase-analytics.service.ts @@ -3,9 +3,9 @@ import { Firebase } from '@ionic-native/firebase/ngx' import { Platform } from 'ionic-angular' import { User } from '../../../shared/models/user' +import { RemoteConfigService } from '../config/remote-config.service' import { LogService } from '../misc/log.service' import { AnalyticsService } from './analytics.service' -import { RemoteConfigService } from '../config/remote-config.service' @Injectable() export class FirebaseAnalyticsService extends AnalyticsService { @@ -25,13 +25,18 @@ export class FirebaseAnalyticsService extends AnalyticsService { const cleanParams = {} - Object.entries(params) - .forEach(([key, value]) => { - const cleanKey = this.crop(key, 40, - `Firebase analytics key ${key} is too long, cropping to 40 characters`) - cleanParams[cleanKey] = this.crop(String(value), 100, - `Firebase analytics value for ${key} is too long, cropping to 100 characters: ${value}`) - }) + Object.entries(params).forEach(([key, value]) => { + const cleanKey = this.crop( + key, + 40, + `Firebase analytics key ${key} is too long, cropping to 40 characters` + ) + cleanParams[cleanKey] = this.crop( + String(value), + 100, + `Firebase analytics value for ${key} is too long, cropping to 100 characters: ${value}` + ) + }) return this.firebase .logEvent(event, cleanParams) @@ -74,15 +79,27 @@ export class FirebaseAnalyticsService extends AnalyticsService { */ setUserProperties(userProperties: User): Promise { + if (!this.platform.is('cordova')) + return Promise.resolve('Could not load firebase') + return Promise.all( - Object.entries(userProperties) - .filter(([k, v]) => k) - .map(([key, value]) => { - return this.firebase.setUserProperty( - this.crop(key, 24, `Firebase User Property name ${key} is too long, cropping`), - this.crop(String(value), 36, `Firebase User Property value ${value} for ${key} is too long, cropping`)) - })) - .then(() => this.remoteConfig.forceFetch()) + Object.entries(userProperties) + .filter(([k, v]) => k) + .map(([key, value]) => { + return this.firebase.setUserProperty( + this.crop( + key, + 24, + `Firebase User Property name ${key} is too long, cropping` + ), + this.crop( + String(value), + 36, + `Firebase User Property value ${value} for ${key} is too long, cropping` + ) + ) + }) + ).then(() => this.remoteConfig.forceFetch()) } setUserId(userId: string): Promise { diff --git a/src/app/pages/auth/services/auth.service.ts b/src/app/pages/auth/services/auth.service.ts index f888e537d..9afe1ea93 100644 --- a/src/app/pages/auth/services/auth.service.ts +++ b/src/app/pages/auth/services/auth.service.ts @@ -5,7 +5,6 @@ import { Injectable } from '@angular/core' import { DefaultManagementPortalURI, - DefaultRefreshTokenRequestBody, DefaultRequestEncodedContentType, DefaultRequestJSONContentType, DefaultSourceTypeModel, @@ -14,8 +13,8 @@ import { } from '../../../../assets/data/defaultConfig' import { ConfigService } from '../../../core/services/config/config.service' import { LogService } from '../../../core/services/misc/log.service' -import { AnalyticsService } from '../../../core/services/usage/analytics.service' import { TokenService } from '../../../core/services/token/token.service' +import { AnalyticsService } from '../../../core/services/usage/analytics.service' import { MetaToken } from '../../../shared/models/token' import { isValidURL } from '../../../shared/utilities/form-validators' @@ -58,8 +57,7 @@ export class AuthService { metaTokenJsonAuth(authObj) { // NOTE: Old QR codes: containing refresh token as JSON - return this.updateURI() - .then(() => JSON.parse(authObj).refreshToken) + return this.updateURI().then(() => JSON.parse(authObj).refreshToken) } updateURI() { @@ -69,8 +67,7 @@ export class AuthService { } registerToken(registrationToken): Promise { - const refreshBody = DefaultRefreshTokenRequestBody + registrationToken - return this.token.register(refreshBody) + return this.token.register(this.token.getRefreshParams(registrationToken)) } getRefreshTokenFromUrl(url): Promise { @@ -84,7 +81,7 @@ export class AuthService { getSubjectInformation(): Promise { return Promise.all([ this.token.getAccessHeaders(DefaultRequestEncodedContentType), - this.token.getDecodedSubject(), + this.token.getDecodedSubject() ]).then(([headers, subject]) => this.http.get(this.getSubjectURI(subject), { headers }).toPromise() ) @@ -101,13 +98,15 @@ export class AuthService { sourceId: this.getSourceId(subjectInformation), humanReadableId: subjectInformation.externalId, enrolmentDate: new Date(subjectInformation.createdDate).getTime(), - baseUrl: baseUrl, + baseUrl: baseUrl }) }) } getSourceId(response) { - const source = response.sources.find(s => s.sourceTypeModel === DefaultSourceTypeModel) + const source = response.sources.find( + s => s.sourceTypeModel === DefaultSourceTypeModel + ) return source !== undefined ? source.sourceId : 'Device not available' } diff --git a/src/app/pages/clinical-tasks/containers/clinical-tasks-page.component.ts b/src/app/pages/clinical-tasks/containers/clinical-tasks-page.component.ts index 984bae0a8..5dd13d9b2 100644 --- a/src/app/pages/clinical-tasks/containers/clinical-tasks-page.component.ts +++ b/src/app/pages/clinical-tasks/containers/clinical-tasks-page.component.ts @@ -20,7 +20,7 @@ export class ClinicalTasksPageComponent { ionViewDidLoad() { this.clinicalTasksService.getClinicalAssessments().then(assessments => { - this.assessments = assessments + this.assessments = assessments.sort((a, b) => a.order - b.order) }) } diff --git a/src/app/pages/home/containers/home-page.component.ts b/src/app/pages/home/containers/home-page.component.ts index 25941ed2a..8daa6d26f 100755 --- a/src/app/pages/home/containers/home-page.component.ts +++ b/src/app/pages/home/containers/home-page.component.ts @@ -85,7 +85,7 @@ export class HomePageComponent implements OnDestroy { this.tasks.then(tasks => { this.checkTaskInterval = setInterval(() => { this.checkForNextTask(tasks) - }, 1000) + }, 1500) }) this.hasClinicalTasks = this.tasksService.evalHasClinicalTasks() this.title = this.tasksService.getPlatformInstanceName() diff --git a/src/app/pages/home/services/tasks.service.ts b/src/app/pages/home/services/tasks.service.ts index 74ea071e5..26569c263 100644 --- a/src/app/pages/home/services/tasks.service.ts +++ b/src/app/pages/home/services/tasks.service.ts @@ -93,8 +93,12 @@ export class TasksService { */ getNextTask(tasks: Task[]): Task | undefined { if (tasks) { - return tasks.find(task => !this.isTaskExpired(task)) + const nextTasksNow = tasks.filter(task => this.isTaskStartable(task)) + if (nextTasksNow.length) { + return nextTasksNow.sort((a, b) => a.order - b.order)[0] + } else return tasks.find(task => !this.isTaskExpired(task)) } + return undefined } getCurrentDateMidnight() { diff --git a/src/app/pages/questions/components/finish/finish.component.html b/src/app/pages/questions/components/finish/finish.component.html index 8f96b9d83..b1febd864 100755 --- a/src/app/pages/questions/components/finish/finish.component.html +++ b/src/app/pages/questions/components/finish/finish.component.html @@ -1,5 +1,4 @@ Completed -

{{ 'FINISH_THANKS' | translate }}

{{ content }}

diff --git a/src/app/pages/questions/components/finish/finish.component.scss b/src/app/pages/questions/components/finish/finish.component.scss index 33b60de40..69deebf29 100755 --- a/src/app/pages/questions/components/finish/finish.component.scss +++ b/src/app/pages/questions/components/finish/finish.component.scss @@ -45,7 +45,7 @@ finish { .pos-bottom { position: absolute; - bottom: 0; + bottom: 32px; left: 0; width: 100%; } diff --git a/src/app/pages/questions/components/introduction/introduction.component.scss b/src/app/pages/questions/components/introduction/introduction.component.scss index 7cf014167..c3fa4f9f4 100644 --- a/src/app/pages/questions/components/introduction/introduction.component.scss +++ b/src/app/pages/questions/components/introduction/introduction.component.scss @@ -2,4 +2,8 @@ introduction { .questionnaire-title { font-size: 4rem; } + + .icons-big { + width: 30%; + } } diff --git a/src/app/pages/questions/components/question/audio-input/audio-input.component.html b/src/app/pages/questions/components/question/audio-input/audio-input.component.html index 2773ffbd8..9ef5c9ff4 100644 --- a/src/app/pages/questions/components/question/audio-input/audio-input.component.html +++ b/src/app/pages/questions/components/question/audio-input/audio-input.component.html @@ -1,14 +1,16 @@ -
- - - +
+
+ + + +
diff --git a/src/app/pages/questions/components/question/audio-input/audio-input.component.scss b/src/app/pages/questions/components/question/audio-input/audio-input.component.scss index 15cd671c1..f79caebd8 100644 --- a/src/app/pages/questions/components/question/audio-input/audio-input.component.scss +++ b/src/app/pages/questions/components/question/audio-input/audio-input.component.scss @@ -1,12 +1,18 @@ audio-input { - display: block; - width: 100%; - .audio-bottom { position: absolute; - top: 75vh; - margin: 8px; - width: 88%; + bottom: 48px; + left: 50%; + width: 80%; + } + + .audio-container { + width: inherit; + height: inherit !important; + } + + button { + left: -50%; } .bt-stop { diff --git a/src/app/pages/questions/components/question/info-screen/info-screen.component.html b/src/app/pages/questions/components/question/info-screen/info-screen.component.html index 0d8ad259f..2790f8a5f 100755 --- a/src/app/pages/questions/components/question/info-screen/info-screen.component.html +++ b/src/app/pages/questions/components/question/info-screen/info-screen.component.html @@ -1,13 +1,7 @@ -
+
- +
@@ -18,11 +12,7 @@

{{ item.content }}

- +
diff --git a/src/app/pages/questions/components/question/info-screen/info-screen.component.scss b/src/app/pages/questions/components/question/info-screen/info-screen.component.scss index 5e9898722..d6d4d2579 100755 --- a/src/app/pages/questions/components/question/info-screen/info-screen.component.scss +++ b/src/app/pages/questions/components/question/info-screen/info-screen.component.scss @@ -1,15 +1,10 @@ info-screen { - display: flex; - width: 100%; + overflow: hidden; .info-card { - margin: 8px; padding: 10px; - max-height: calc(100% - 88px); - width: inherit; - height: calc(100vh - 184px); - -webkit-border-radius: 5px; - border-radius: 5px; + -webkit-border-radius: 8px; + border-radius: 8px; background-color: $cl-primary-20; } @@ -29,11 +24,6 @@ info-screen { padding-bottom: 32%; } - .info-card-sm { - max-height: calc(94% - 88px); - height: calc(95vh - 184px); - } - .info-heading { color: $cl-light-60; } @@ -50,7 +40,8 @@ info-screen { .icon { position: relative; - margin: -40px -8px; + float: right; + margin: -36px 8px; width: inherit; text-align: right; } diff --git a/src/app/pages/questions/components/question/info-screen/info-screen.component.ts b/src/app/pages/questions/components/question/info-screen/info-screen.component.ts index 38e9310d6..d33b745b6 100755 --- a/src/app/pages/questions/components/question/info-screen/info-screen.component.ts +++ b/src/app/pages/questions/components/question/info-screen/info-screen.component.ts @@ -7,7 +7,7 @@ import { Output, ViewChild } from '@angular/core' -import { Content } from 'ionic-angular' +import { Content, Platform } from 'ionic-angular' import { InfoItem, Section } from '../../../../../shared/models/question' @@ -36,9 +36,14 @@ export class InfoScreenComponent implements OnInit, OnChanges { isThincItReminder = false items: InfoItem[] = Array() showScrollButton: boolean + height = '92%' + + constructor(private platform: Platform) {} ngOnInit() { this.initSections() + if (this.platform.is('ios')) this.height = '64vh' + if (this.hasFieldLabel) this.height = '88%' } ngOnChanges() { @@ -67,7 +72,10 @@ export class InfoScreenComponent implements OnInit, OnChanges { } onScroll(event) { - if (event.scrollTop >= (event.scrollHeight - event.contentHeight) * 0.8) { + if ( + event && + event.scrollTop >= (event.scrollHeight - event.contentHeight) * 0.8 + ) { this.emitTimestamp() this.showScrollButton = false } else this.showScrollButton = true diff --git a/src/app/pages/questions/components/question/question.component.html b/src/app/pages/questions/components/question/question.component.html index 191c35913..57417606c 100755 --- a/src/app/pages/questions/components/question/question.component.html +++ b/src/app/pages/questions/components/question/question.component.html @@ -1,100 +1,103 @@ -
-

{{ question.section_header }}

-
-

- {{ question.field_label }} -

+
+
+

{{ question.section_header }}

+
+

+ {{ question.field_label }} +

+
+
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
- -
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- -
+
+ +
-
- - +
+ + +
diff --git a/src/app/pages/questions/components/question/question.component.scss b/src/app/pages/questions/components/question/question.component.scss index be3aa95e1..d88451484 100755 --- a/src/app/pages/questions/components/question/question.component.scss +++ b/src/app/pages/questions/components/question/question.component.scss @@ -1,5 +1,10 @@ question { - text-align: left; + .content { + display: flex; + flex-direction: column; + height: 96%; + text-align: left; + } p { margin-top: 8px; @@ -13,7 +18,7 @@ question { } .field-container { - max-height: 56vh !important; + max-height: 50vh !important; } .rangeformat { @@ -21,8 +26,18 @@ question { } .fade { - display: block; opacity: 0.2; pointer-events: none; } + + .header { + flex-grow: 0; + } + + .input { + flex-grow: 1; + overflow-x: hidden; + overflow-y: auto; + padding: 4px; + } } diff --git a/src/app/pages/questions/components/question/range-info-input/range-info-input.component.html b/src/app/pages/questions/components/question/range-info-input/range-info-input.component.html index 7f79cff13..f14212ad1 100755 --- a/src/app/pages/questions/components/question/range-info-input/range-info-input.component.html +++ b/src/app/pages/questions/components/question/range-info-input/range-info-input.component.html @@ -1,12 +1,6 @@ - +
-
-

-
+

diff --git a/src/app/pages/questions/components/question/range-info-input/range-info-input.component.scss b/src/app/pages/questions/components/question/range-info-input/range-info-input.component.scss index 8d97cb533..43c5947ca 100755 --- a/src/app/pages/questions/components/question/range-info-input/range-info-input.component.scss +++ b/src/app/pages/questions/components/question/range-info-input/range-info-input.component.scss @@ -1,20 +1,16 @@ range-info-input { - display: block; - width: 100%; + overflow: hidden; range-input { - margin-bottom: 32px; + margin-bottom: 24px; } .info-card { - overflow: scroll; - margin: 8px; + overflow: auto; padding: 10px; - max-height: calc(100% - 360px); - width: initial; - height: calc(100vh - 450px); - -webkit-border-radius: 5px; - border-radius: 5px; + max-height: 60%; + -webkit-border-radius: 8px; + border-radius: 8px; background-color: $cl-primary-20; } diff --git a/src/app/pages/questions/components/question/timed-test/timed-test.component.scss b/src/app/pages/questions/components/question/timed-test/timed-test.component.scss index 8c1bacffc..cd982921a 100755 --- a/src/app/pages/questions/components/question/timed-test/timed-test.component.scss +++ b/src/app/pages/questions/components/question/timed-test/timed-test.component.scss @@ -1,21 +1,19 @@ timed-test { - display: flex; - width: 100%; + overflow: hidden; + text-align: center; .test-card { - margin: 8px; + overflow: hidden; padding: 10px; - max-height: calc(94% - 88px); - width: inherit; - height: calc(95vh - 184px); - -webkit-border-radius: 5px; - border-radius: 5px; + height: 92%; + -webkit-border-radius: 8px; + border-radius: 8px; background-color: $cl-primary-20; - text-align: center; } ion-content { background: none; + text-align: center !important; } .heading { diff --git a/src/app/pages/questions/containers/questions-page.component.scss b/src/app/pages/questions/containers/questions-page.component.scss index 0e667adba..f70b23add 100755 --- a/src/app/pages/questions/containers/questions-page.component.scss +++ b/src/app/pages/questions/containers/questions-page.component.scss @@ -4,8 +4,8 @@ page-questions { } ion-slide { - overflow-x: hidden !important; - overflow-y: auto !important; + overflow: hidden !important; padding: 24px; + height: 96vh !important; } } diff --git a/src/app/pages/questions/containers/questions-page.component.spec.ts b/src/app/pages/questions/containers/questions-page.component.spec.ts index 6843cea26..e5dbee38d 100644 --- a/src/app/pages/questions/containers/questions-page.component.spec.ts +++ b/src/app/pages/questions/containers/questions-page.component.spec.ts @@ -2,7 +2,6 @@ import { ComponentFixture, TestBed, async } from '@angular/core/testing' import { NavController, NavParams } from 'ionic-angular' import { NavParamsMock } from 'ionic-mocks' -import { DefaultTaskTest } from '../../../../assets/data/defaultConfig' import { AppModule } from '../../../app.module' import { QuestionsService } from '../services/questions.service' import { QuestionsPageComponent } from './questions-page.component' diff --git a/src/app/pages/questions/containers/questions-page.component.ts b/src/app/pages/questions/containers/questions-page.component.ts index 8aec202d3..69687c296 100644 --- a/src/app/pages/questions/containers/questions-page.component.ts +++ b/src/app/pages/questions/containers/questions-page.component.ts @@ -2,8 +2,10 @@ import { Component, OnInit, ViewChild } from '@angular/core' import { Insomnia } from '@ionic-native/insomnia/ngx' import { NavController, NavParams, Platform, Slides } from 'ionic-angular' +import { LocalizationService } from '../../../core/services/misc/localization.service' import { UsageService } from '../../../core/services/usage/usage.service' import { UsageEventType } from '../../../shared/enums/events' +import { LocKeys } from '../../../shared/enums/localisations' import { Assessment } from '../../../shared/models/assessment' import { Question } from '../../../shared/models/question' import { Task } from '../../../shared/models/task' @@ -44,7 +46,8 @@ export class QuestionsPageComponent implements OnInit { private questionsService: QuestionsService, private usage: UsageService, private platform: Platform, - private insomnia: Insomnia + private insomnia: Insomnia, + private localization: LocalizationService ) { this.platform.registerBackButtonAction(() => { this.sendCompletionLog() @@ -61,7 +64,10 @@ export class QuestionsPageComponent implements OnInit { this.introduction = res.introduction this.showIntroductionScreen = res.assessment.showIntroduction this.questions = res.questions - this.endText = res.endText + this.endText = + res.endText && res.endText.length + ? res.endText + : this.localization.translateKey(LocKeys.FINISH_THANKS) this.isLastTask = res.isLastTask this.assessment = res.assessment this.taskType = res.type diff --git a/src/app/pages/questions/services/audio-record.service.ts b/src/app/pages/questions/services/audio-record.service.ts index 06a8dd959..9302439c1 100644 --- a/src/app/pages/questions/services/audio-record.service.ts +++ b/src/app/pages/questions/services/audio-record.service.ts @@ -34,8 +34,10 @@ export class AudioRecordService { } stopAudioRecording() { - this.audio.stopRecord() - this.isRecording = false + if (this.isRecording) { + this.audio.stopRecord() + this.isRecording = false + } } getFilePath() { diff --git a/src/app/pages/settings/components/cache-send-modal/cache-send-modal.component.html b/src/app/pages/settings/components/cache-send-modal/cache-send-modal.component.html index 8109dee9d..f8b59648e 100644 --- a/src/app/pages/settings/components/cache-send-modal/cache-send-modal.component.html +++ b/src/app/pages/settings/components/cache-send-modal/cache-send-modal.component.html @@ -34,7 +34,9 @@
-
  • {{ e.message }}
  • + +
  • {{ e.message }}
  • +
    diff --git a/src/app/pages/settings/components/cache-send-modal/cache-send-modal.component.scss b/src/app/pages/settings/components/cache-send-modal/cache-send-modal.component.scss index cc8f89040..40d511fb1 100644 --- a/src/app/pages/settings/components/cache-send-modal/cache-send-modal.component.scss +++ b/src/app/pages/settings/components/cache-send-modal/cache-send-modal.component.scss @@ -29,9 +29,8 @@ cache-send-modal { } li { - overflow: hidden; + overflow-x: scroll; padding: 4px; - text-overflow: ellipsis; white-space: nowrap; font-size: 12px; } diff --git a/src/app/pages/splash/containers/splash-page.component.ts b/src/app/pages/splash/containers/splash-page.component.ts index 8c4f6837d..50408d229 100644 --- a/src/app/pages/splash/containers/splash-page.component.ts +++ b/src/app/pages/splash/containers/splash-page.component.ts @@ -1,14 +1,18 @@ import { Component } from '@angular/core' -import { NavController, NavParams } from 'ionic-angular' +import { NavController, NavParams, Platform } from 'ionic-angular' +import { DefaultPackageName } from '../../../../assets/data/defaultConfig' import { AlertService } from '../../../core/services/misc/alert.service' import { LocalizationService } from '../../../core/services/misc/localization.service' import { UsageService } from '../../../core/services/usage/usage.service' +import { ConfigEventType } from '../../../shared/enums/events' import { LocKeys } from '../../../shared/enums/localisations' import { EnrolmentPageComponent } from '../../auth/containers/enrolment-page.component' import { HomePageComponent } from '../../home/containers/home-page.component' import { SplashService } from '../services/splash.service' +declare var window + @Component({ selector: 'page-splash', templateUrl: 'splash-page.component.html' @@ -21,7 +25,8 @@ export class SplashPageComponent { private splash: SplashService, private alertService: AlertService, private localization: LocalizationService, - private usage: UsageService + private usage: UsageService, + private platform: Platform ) { this.splash .evalEnrolment() @@ -42,7 +47,11 @@ export class SplashPageComponent { ) return this.splash.sendMissedQuestionnaireLogs() }) - .catch(e => this.showFetchConfigFail(e)) + .catch(e => + e.message == ConfigEventType.APP_UPDATE_AVAILABLE + ? this.showAppUpdateAvailable() + : this.showFetchConfigFail(e) + ) .then(() => this.navCtrl.setRoot(HomePageComponent)) } @@ -67,6 +76,30 @@ export class SplashPageComponent { }) } + showAppUpdateAvailable() { + this.alertService.showAlert({ + title: this.localization.translateKey(LocKeys.STATUS_UPDATE_AVAILABLE), + message: this.localization.translateKey( + LocKeys.STATUS_UPDATE_AVAILABLE_DESC + ), + buttons: [ + { + text: this.localization.translateKey(LocKeys.BTN_UPDATE), + handler: () => { + this.openApplicationStore() + } + } + ] + }) + } + + openApplicationStore() { + const url = this.platform.is('ios') + ? 'itms-apps://itunes.apple.com/app/' + : 'market://details?id=' + DefaultPackageName + window.location.replace(url) + } + enrol() { this.splash.reset().then(() => this.navCtrl.setRoot(EnrolmentPageComponent)) } diff --git a/src/app/pages/splash/services/splash.service.ts b/src/app/pages/splash/services/splash.service.ts index 147598eb3..8961392f1 100644 --- a/src/app/pages/splash/services/splash.service.ts +++ b/src/app/pages/splash/services/splash.service.ts @@ -29,18 +29,17 @@ export class SplashService { } sendMissedQuestionnaireLogs() { - return this.schedule - .getIncompleteTasks() - .then(tasks => - Promise.all( - tasks - .slice(0, DefaultNumberOfCompletionLogsToSend) - .map(task => - this.usage - .sendCompletionLog(task, 0) - .then(() => this.schedule.updateTaskToReportedCompletion(task)) - ) - ) + return this.schedule.getIncompleteTasks().then(tasks => + Promise.all( + tasks + .filter(t => !t.reportedCompletion) + .slice(0, DefaultNumberOfCompletionLogsToSend) + .map(task => + this.usage + .sendCompletionLog(task, 0) + .then(() => this.schedule.updateTaskToReportedCompletion(task)) + ) ) + ) } } diff --git a/src/app/shared/enums/events.ts b/src/app/shared/enums/events.ts index f6e5648be..65ceace83 100644 --- a/src/app/shared/enums/events.ts +++ b/src/app/shared/enums/events.ts @@ -29,6 +29,7 @@ export enum DataEventType { export enum ConfigEventType { PROTOCOL_CHANGE = 'protocol_change', APP_VERSION_CHANGE = 'app_version_change', + APP_UPDATE_AVAILABLE = 'app_update_available', TIMEZONE_CHANGE = 'timezone_change', ERROR = 'config_error', APP_RESET = 'app_reset', diff --git a/src/app/shared/enums/localisations.ts b/src/app/shared/enums/localisations.ts index ab6fd70ab..0a6c074ca 100644 --- a/src/app/shared/enums/localisations.ts +++ b/src/app/shared/enums/localisations.ts @@ -80,10 +80,15 @@ export class LocKeys { static BTN_YES = new LocKeys('BTN_YES') static BTN_NO = new LocKeys('BTN_NO') static BTN_TRY_AGAIN = new LocKeys('BTN_TRY_AGAIN') + static BTN_UPDATE = new LocKeys('BTN_UPDATE') static STATUS_LOADING = new LocKeys('STATUS_LOADING') static STATUS_SUCCESS = new LocKeys('STATUS_SUCCESS') static STATUS_FAILURE = new LocKeys('STATUS_FAILURE') static STATUS_NOW = new LocKeys('STATUS_NOW') + static STATUS_UPDATE_AVAILABLE = new LocKeys('STATUS_UPDATE_AVAILABLE') + static STATUS_UPDATE_AVAILABLE_DESC = new LocKeys( + 'STATUS_UPDATE_AVAILABLE_DESC' + ) static NOTIFICATION_TEST_REMINDER_NOW = new LocKeys( 'NOTIFICATION_TEST_REMINDER_NOW' ) diff --git a/src/app/shared/models/assessment.ts b/src/app/shared/models/assessment.ts index 0860c8b9f..d321283d0 100755 --- a/src/app/shared/models/assessment.ts +++ b/src/app/shared/models/assessment.ts @@ -14,6 +14,7 @@ export interface Assessment { isDemo?: boolean questions: Question[] showInCalendar?: boolean + order?: number } export interface QuestionnaireMetadata { diff --git a/src/app/shared/models/task.ts b/src/app/shared/models/task.ts index 5ca93be85..32088fc66 100755 --- a/src/app/shared/models/task.ts +++ b/src/app/shared/models/task.ts @@ -11,10 +11,11 @@ export interface Task { completionWindow: number warning: string isClinical: boolean - notifications?: SingleNotification[] - timeCompleted?: number - showInCalendar?: boolean - isDemo?: boolean + notifications: SingleNotification[] + timeCompleted: number + showInCalendar: boolean + isDemo: boolean + order: number } export interface TasksProgress { diff --git a/src/app/shared/utilities/parse-version.ts b/src/app/shared/utilities/parse-version.ts new file mode 100644 index 000000000..73ab29896 --- /dev/null +++ b/src/app/shared/utilities/parse-version.ts @@ -0,0 +1,10 @@ +export function parseVersion(data) { + // NOTE: Based on current Google Play Store page pattern + // TODO: Add support for iOS + const playstorePattern = /([0-9]*[.][0-9]*[.][0-9]*)<\/span>/g + const versionPattern = /([0-9]*[.][0-9]*[.][0-9]*)/ + return data + .match(playstorePattern) + .toString() + .match(versionPattern)[0] +} diff --git a/src/app/shared/utilities/time.ts b/src/app/shared/utilities/time.ts index 693661c31..7e687e64f 100644 --- a/src/app/shared/utilities/time.ts +++ b/src/app/shared/utilities/time.ts @@ -1,6 +1,6 @@ import { - DefaultScheduleYearCoverage, - DefaultTimeInterval + DefaultScheduleTimeInterval, + DefaultScheduleYearCoverage } from '../../../assets/data/defaultConfig' import { TimeInterval } from '../../shared/models/protocol' @@ -90,8 +90,12 @@ export function timeIntervalToMillis(interval: TimeInterval): number { return getMilliseconds({ years: DefaultScheduleYearCoverage }) } const unit = - interval.unit in TIME_UNIT_MILLIS ? interval.unit : DefaultTimeInterval.unit - const amount = interval.amount ? interval.amount : DefaultTimeInterval.amount + interval.unit in TIME_UNIT_MILLIS + ? interval.unit + : DefaultScheduleTimeInterval.unit + const amount = interval.amount + ? interval.amount + : DefaultScheduleTimeInterval.amount return amount * TIME_UNIT_MILLIS[unit] } diff --git a/src/assets/data/defaultConfig.ts b/src/assets/data/defaultConfig.ts index d6ab42fa3..d7849cc09 100755 --- a/src/assets/data/defaultConfig.ts +++ b/src/assets/data/defaultConfig.ts @@ -7,32 +7,41 @@ import { import { Task } from '../../app/shared/models/task' import { DefaultSourceProducerAndSecretExport } from './secret' -// DEFAULT SETTINGS -export const DefaultSettingsNotifications: NotificationSettings = { - sound: true, - vibration: false, - nightMode: true +// DEFAULT APP INFO + +// *Title of RADAR base platform instance (REMOTE CONFIG KEY: `platform_instance`) +export const DefaultPlatformInstance = 'RADAR-CNS' + +// *Default app version +export const DefaultAppVersion = '0.7.2-alpha' + +// *Default Android package name +export const DefaultPackageName = 'org.phidatalab.radar_armt' + +// *Default iOS app id +export const DefaultAppId = '' + +// DEFAULT SOURCE INFO +// *This is the default source info and description for the aRMT app in RADAR base. +// *NOTE: These details must match the schema specification files. + +export const DefaultSourcePrefix = 'aRMT' +export const DefaultSourceTypeModel: string = `${DefaultSourcePrefix}-App` +export const DefaultSourceTypeRegistrationBody = { + sourceTypeCatalogVersion: '1.4.3', + sourceTypeModel: DefaultSourceTypeModel, + sourceTypeProducer: 'RADAR' } -export const DefaultSettingsWeeklyReport: WeeklyReportSubSettings[] = [ - { - name: LocKeys.MEASURE_PROGRESS.toString(), - show: false - }, - { - name: LocKeys.MEASURE_STEPS.toString(), - show: false - }, - { - name: LocKeys.MEASURE_HEART_RATE.toString(), - show: false - } -] +// DEFAULT SCHEDULE SETUP + +// *Default general task completion window or time window in which the task is available to answer (1 day in ms) +export const DefaultTaskCompletionWindow = 86400000 -// DEFAULT SETUP -export const DefaultTaskCompletionWindow = 86400000 // 1 day in ms -export const DefaultESMCompletionWindow = 600000 // 10 mins in ms +// *Default ESM completion window (10 mins in ms) +export const DefaultESMCompletionWindow = 600000 +// *Default sample task export const DefaultTask: Task = { index: 0, completed: false, @@ -44,111 +53,76 @@ export const DefaultTask: Task = { completionWindow: DefaultTaskCompletionWindow, warning: '', isClinical: false, - notifications: [] -} - -export const DefaultTaskTest: Task = { - index: 0, - completed: false, - reportedCompletion: false, - timestamp: 0, - name: 'TEST', - nQuestions: 0, - estimatedCompletionTime: 0, - completionWindow: DefaultTaskCompletionWindow, - warning: '', - isClinical: false, - notifications: [] + notifications: [], + timeCompleted: 0, + showInCalendar: true, + isDemo: false, + order: 0 } -export const DefaultLanguage: LanguageSetting = { - label: '', - value: '' -} +// *Default schedule coverage in years (length of schedule to generate tasks until) +export const DefaultScheduleYearCoverage: number = 2 -export const DefaultSettingsSupportedLanguages: LanguageSetting[] = [ - { - label: LocKeys.LANGUAGE_ENGLISH.toString(), - value: 'en' - }, - { - label: LocKeys.LANGUAGE_ITALIAN.toString(), - value: 'it' - }, - { - label: LocKeys.LANGUAGE_SPANISH.toString(), - value: 'es' - }, - { - label: LocKeys.LANGUAGE_DUTCH.toString(), - value: 'nl' - }, - { - label: LocKeys.LANGUAGE_DANISH.toString(), - value: 'da' - }, - { - label: LocKeys.LANGUAGE_GERMAN.toString(), - value: 'de' - } -] +// *Default time interval of protocol +export const DefaultScheduleTimeInterval = { unit: 'day', amount: 1 } -export const LanguageMap = { - en: LocKeys.LANGUAGE_ENGLISH.toString(), - it: LocKeys.LANGUAGE_ITALIAN.toString(), - es: LocKeys.LANGUAGE_SPANISH.toString(), - nl: LocKeys.LANGUAGE_DUTCH.toString(), - da: LocKeys.LANGUAGE_DANISH.toString(), - de: LocKeys.LANGUAGE_GERMAN.toString() -} +// *Default schedule/protocol version +export const DefaultScheduleVersion = '0.3.10' -export const DefaultAppVersion: string = '0.5.11.1-alpha' +// *Default max number of completion logs to send on app start +export const DefaultNumberOfCompletionLogsToSend = 10 -export const DefaultScheduleVersion = '0.3.10' +// DEFAULT NOTIFICATION SETUP -export const DefaultScheduleYearCoverage: number = 2 // years +// *Default notification type (either 'FCM' or 'LOCAL' notifications) +export const DefaultNotificationType: string = 'FCM' -export const DefaultScheduleReportRepeat: number = 7 // days +// *Default FCM sender ID from Firebase settings +export const FCMPluginProjectSenderId: string = '430900191220' -export const DefaultNotificationType: string = 'FCM' // choose from 'FCM' or 'LOCAL' +// *Default maximum upstream retries when sending fails export const DefaultMaxUpstreamResends = 100 -export const DefaultNumberOfNotificationsToSchedule: number = 100 // -export const DefaultNumberOfNotificationsToRescue: number = 12 // -export const FCMPluginProjectSenderId: string = '430900191220' -export const DefaultNotificationRefreshTime: number = 900000 // 15 mins in ms -export const DefaultNotificationTtlMinutes: number = 10 -export const DefaultSourcePrefix = 'aRMT' -export const DefaultSourceTypeModel: string = `${DefaultSourcePrefix}-App` -export const DefaultSourceTypeRegistrationBody = { - sourceTypeCatalogVersion: '1.4.3', - sourceTypeModel: DefaultSourceTypeModel, - sourceTypeProducer: 'RADAR' - // "deviceTypeId": 1104 -} +// *Default number of notifications to schedule +export const DefaultNumberOfNotificationsToSchedule: number = 100 -export const DefaultEndPoint: string = - 'https://radar-cns-platform.rosalind.kcl.ac.uk' +// *Default length of time to wait before refreshing notifications (15 mins in ms) +export const DefaultNotificationRefreshTime: number = 900000 -export const DefaultPlatformInstance = 'RADAR-CNS' +// *Default TTL or lifespan of FCM notifications (will try to send during lifespan before being discarded) (10 mins) +export const DefaultNotificationTtlMinutes: number = 10 -// GITHUB SOURCES +// DEFAULT GITHUB SOURCES export const GIT_API_URI = 'https://api.github.com/repos' +// *The Github repository where the protocols are located export const DefaultProtocolGithubRepo = 'RADAR-Base/RADAR-aRMT-protocols' + +// *The name of the branch where the protocol definitions should be read from (REMOTE CONFIG KEY: `protocol_branch`) export const DefaultProtocolBranch = 'master' + +// *The path inside a project name that should be read for a protocol (REMOTE CONFIG KEY: `protocol_path`) export const DefaultProtocolPath = `protocol.json` + +// *The full protocol endpoint (REMOTE CONFIG KEY: `protocol_base_url`) export const DefaultProtocolEndPoint = [ GIT_API_URI, DefaultProtocolGithubRepo, 'contents' ].join('/') +// *The name of the repository where the questionnaire schemas are located export const DefaultSchemaGithubRepo = 'RADAR-Base/RADAR-Schemas' + +// *The name of the branch in the schema repository export const DefaultSchemaBranch = 'master' + +// *The path to the schema specifications file // tslint:disable-next-line: max-line-length export const DefaultSchemaSpecPath = `specifications/active/${DefaultSourcePrefix}-${DefaultSourceTypeRegistrationBody.sourceTypeCatalogVersion}.yml?ref=${DefaultSchemaBranch}` + +// *The URL of the Kafka topic specification (REMOTE CONFIG KEY: `kafka_specification_url`) export const DefaultSchemaSpecEndpoint = [ GIT_API_URI, DefaultSchemaGithubRepo, @@ -156,53 +130,129 @@ export const DefaultSchemaSpecEndpoint = [ DefaultSchemaSpecPath ].join('/') +// DEFAULT AUTH DATA + +// *The client id and secret for OAuth authorisation with the Management Portal const oauthParts = DefaultSourceProducerAndSecretExport.split(':') +// * Default oAuth client id (REMOTE CONFIG KEY: `oauth_client_id`) export const DefaultOAuthClientId = oauthParts.shift() -// TODO: Use empty client secret https://github.com/RADAR-base/RADAR-Questionnaire/issues/140 -export const DefaultOAuthClientSecret = oauthParts.join(':') -export const DefaultPackageName = 'org.phidatalab.radar_armt' +// * Default oAuth client secret (REMOTE CONFIG KEY: `oauth_client_secret`) +export const DefaultOAuthClientSecret = oauthParts.join(':') -// CONFIG SERVICE +// *Default length of time to wait before refreshing tokens (REMOTE CONFIG KEY: `oauth_refresh_seconds`) +export const DefaultTokenRefreshSeconds = 1800 // 30 minutes in s -export const DefaultQuestionnaireTypeURI = '_armt' -export const DefaultQuestionnaireFormatURI = '.json' +// DEFAULT URI -// AUTH SERVICE +// *The Default endpoint where the RADAR-base platform is hosted +export const DefaultEndPoint: string = + 'https://radar-cns-platform.rosalind.kcl.ac.uk' export const DefaultManagementPortalURI = '/managementportal' export const DefaultRefreshTokenURI = '/oauth/token' export const DefaultSubjectsURI = '/api/subjects/' export const DefaultMetaTokenURI: string = '/api/meta-token/' - -export const DefaultRequestEncodedContentType = - 'application/x-www-form-urlencoded' -export const DefaultRequestJSONContentType = 'application/json' -export const DefaultRefreshTokenRequestBody = - 'grant_type=refresh_token&refresh_token=' - export const DefaultEnrolmentBaseURL = DefaultEndPoint + DefaultManagementPortalURI +export const DefaultKafkaURI = '/kafka' +export const DefaultQuestionnaireTypeURI = '_armt' +export const DefaultQuestionnaireFormatURI = '.json' -export const DefaultTokenRefreshSeconds = 1800 // 30 minutes in s +export const DefaultGooglePlaystoreAppURL = + 'https://play.google.com/store/apps/details?id=' +export const DefaultAppleAppStoreAppURL = 'https://apps.apple.com/app/' + +// DEFAULT HTTP REQUEST VALUES -export const DefaultTimeInterval = { unit: 'day', amount: 1 } +// *Default HTTP request encoded content type +export const DefaultRequestEncodedContentType = + 'application/x-www-form-urlencoded' -// KAFKA +// *Default HTTP request JSON content type +export const DefaultRequestJSONContentType = 'application/json' -export const DefaultKafkaURI = '/kafka' +// *Default HTTP request Kafka content type export const DefaultKafkaRequestContentType = 'application/vnd.kafka.avro.v2+json' + +// *Default HTTP request client accept type export const DefaultClientAcceptType = 'application/vnd.kafka.v2+json, application/vnd.kafka+json; q=0.9, application/json; q=0.8' -export const DefaultNumberOfCompletionLogsToSend = 10 - -// AUDIO TASK +// DEFAULT AUDIO INPUT SETUP +// *Default audio input/recording attempts allowed export const DefaultMaxAudioAttemptsAllowed = 5 + +// *Default audio recording settings (sampling rate: 44.1kHz, 32kHz, 16kHz, 12kHz, or 8kHz) export const DefaultAudioRecordOptions = { SampleRate: 16000, NumberOfChannels: 1 } + +// DEFAULT GENERAL SETUP +// *Default notification, report, and language settings + +export const DefaultSettingsNotifications: NotificationSettings = { + sound: true, + vibration: false, + nightMode: true +} + +export const DefaultSettingsWeeklyReport: WeeklyReportSubSettings[] = [ + { + name: LocKeys.MEASURE_PROGRESS.toString(), + show: false + }, + { + name: LocKeys.MEASURE_STEPS.toString(), + show: false + }, + { + name: LocKeys.MEASURE_HEART_RATE.toString(), + show: false + } +] + +export const DefaultLanguage: LanguageSetting = { + label: LocKeys.LANGUAGE_ENGLISH.toString(), + value: 'en' +} + +export const DefaultSettingsSupportedLanguages: LanguageSetting[] = [ + { + label: LocKeys.LANGUAGE_ENGLISH.toString(), + value: 'en' + }, + { + label: LocKeys.LANGUAGE_ITALIAN.toString(), + value: 'it' + }, + { + label: LocKeys.LANGUAGE_SPANISH.toString(), + value: 'es' + }, + { + label: LocKeys.LANGUAGE_DUTCH.toString(), + value: 'nl' + }, + { + label: LocKeys.LANGUAGE_DANISH.toString(), + value: 'da' + }, + { + label: LocKeys.LANGUAGE_GERMAN.toString(), + value: 'de' + } +] + +export const LanguageMap = { + en: LocKeys.LANGUAGE_ENGLISH.toString(), + it: LocKeys.LANGUAGE_ITALIAN.toString(), + es: LocKeys.LANGUAGE_SPANISH.toString(), + nl: LocKeys.LANGUAGE_DUTCH.toString(), + da: LocKeys.LANGUAGE_DANISH.toString(), + de: LocKeys.LANGUAGE_GERMAN.toString() +} diff --git a/src/assets/data/liveinput_android.conf b/src/assets/data/liveinput_android.conf deleted file mode 100644 index 2bc417207..000000000 --- a/src/assets/data/liveinput_android.conf +++ /dev/null @@ -1,44 +0,0 @@ -// This file is part of openSMILE. -// (c) 2016 by audEERING GmbH. All rights reserved. -// See file COPYING for details on licensing terms. -// -// Written by Florian Eyben. fe@audeering.com - -[componentInstances:cComponentManager] -instance[dataMemory].type=cDataMemory -instance[waveIn].type=cOpenslesSource -instance[floatSink].type=cFloatSink -;instance[jniMessageInterface].type = cJniMessageInterface -printLevelStats = 5 -profiling = 0 -nThreads = 1 - -[waveIn:cOpenslesSource] -writer.dmLevel=wave -monoMixdown = 0 -writer.levelconf.growDyn = 0 -writer.levelconf.isRb = 1 -writer.levelconf.nT = 32000 -sampleRate = 16000 -nBits = 16 -channels = 1 -audioBuffersize = 256 - -\{features.conf.inc} -\{messages.conf.inc} - -[floatSink:cFloatSink] -reader.dmLevel = melspec;energyB -; filename = melspec;energyB -filename = \cm[outputfile(O){/storage/emulated/0/Android/data/com.audeering.opensmile.androidtemplate/files/test3.bin}:file name of the output bin file] -append = 0 -timestamp = 1 -number = 1 -printZeroAtEnd = 1 -nCol =1 - -;[jniMessageInterface:cJniMessageInterface] -;sendMessagesInTick = 1 -;JNIcallbackClass = com/audeering/opensmile/androidtemplate/SmileJNI -;JNIstringReceiverMethod = receiveText - diff --git a/src/assets/data/localisations.ts b/src/assets/data/localisations.ts index a200f4ee0..1bc2b65cb 100644 --- a/src/assets/data/localisations.ts +++ b/src/assets/data/localisations.ts @@ -498,7 +498,7 @@ export const Localisations = { nl: 'Begin' }, BTN_STOP: { - da: 'Stoppe', + da: 'Stop', de: 'Stoppen', en: 'Stop', es: 'Detener', @@ -606,11 +606,11 @@ export const Localisations = { de: 'Ja', en: 'Yes', es: 'Sí', - it: 'sì', + it: 'Sì', nl: 'Ja' }, BTN_NO: { - da: 'Ingen', + da: 'Nej', de: 'Nein', en: 'No', es: 'No', @@ -619,11 +619,19 @@ export const Localisations = { }, BTN_TRY_AGAIN: { da: 'Prøv igen', - de: 'Versuchen Sie es nochmal', + de: 'Erneut versuchen', en: 'Try again', - es: 'Inténtalo de nuevo', - it: 'Riprova', - nl: 'Probeer het opnieuw' + es: 'Volver a intentar', + it: 'Prova di nuovo', + nl: 'Probeer opnieuw' + }, + BTN_UPDATE: { + da: 'Opdater', + de: 'Aktualisieren', + en: 'Update', + es: 'Actualizar', + it: 'Aggiorna', + nl: 'Bijwerken' }, STATUS_LOADING: { da: 'Indlæser', @@ -657,6 +665,22 @@ export const Localisations = { it: 'Non riuscuto', nl: 'Mislukt' }, + STATUS_UPDATE_AVAILABLE: { + da: 'Ny Version Tilgængelig', + de: 'Neue Version Verfügbar', + en: 'New Version Available', + es: 'Nueva Versión Disponible', + it: 'Nuova Versione Disponibile', + nl: 'Nieuwe Versie Beschikbaar' + }, + STATUS_UPDATE_AVAILABLE_DESC: { + da: 'Opdater din app, før du fortsætter.', + de: 'Bitte aktualisieren Sie Ihre App, bevor Sie fortfahren.', + en: 'Please update your app before continuing.', + es: 'Actualice su aplicación antes de continuar.', + it: `Aggiorna l'app prima di continuare.`, + nl: 'Werk uw app bij voordat u doorgaat.' + }, NOTIFICATION_TEST_REMINDER_NOW: { da: 'Testmeddelelse', de: 'Testbenachrichtigung', @@ -997,7 +1021,7 @@ export const Localisations = { nl: 'Taak is onderbroken. Start de taak opnieuw.' }, AUDIO_TASK_ATTEMPT_ALERT: { - da: 'Forsøg tilbageværende', + da: 'Tilbageværende forsøg', de: 'Verbleibende Versuche', en: 'Attempts remaining', es: 'Intentos restantes', diff --git a/yarn.lock b/yarn.lock index b0fea7b29..ea665fc3c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -358,129 +358,129 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@ionic-native/android-permissions@^5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/android-permissions/-/android-permissions-5.14.0.tgz#0ef85bfc83c0e3e0852d0468101e6cb67a3d39b6" - integrity sha512-w5WNpr+r073ujO6zy6IRT93WZ/m7d7//GlFVux3x7pAc18asz7sRjlz3aOwwmudBXb7zGzyaIJkZJl5Rp/1CFQ== +"@ionic-native/android-permissions@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/android-permissions/-/android-permissions-5.15.0.tgz#052b2f8bf55eaa80ac959098cd688af8ca0e5236" + integrity sha512-ViXUwGZYFBTipSaKEgS9xShpEbk4b5ZK1C5vii3aSPk6oBXOfpHnoGW/vwMigiokZ6/pliRn3kvdL1O7oMqjaA== dependencies: "@types/cordova" latest -"@ionic-native/app-version@^5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/app-version/-/app-version-5.14.0.tgz#3aa90419980e18e509f2798c639000f2bb038b7e" - integrity sha512-nhopCUxbINg+sVbPqIatdOgKAM3+mEHFkGDhYUKE2fxvHXQ/GvVP88eLvdMb37X9FPeZ+1BL9XlIGXvIxT/D/g== +"@ionic-native/app-version@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/app-version/-/app-version-5.15.0.tgz#1c52151d3f6a5f01c025757f54d8c727b0f1f67a" + integrity sha512-VzfUfp/AiRuet7dwcnOQ9vsRCzTgFYHbcR5pmWytcEYYds0asqWNnPOgoT9LJ74zbsrtV1TKQMmk/P9OApZtzQ== dependencies: "@types/cordova" latest -"@ionic-native/background-mode@^5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/background-mode/-/background-mode-5.14.0.tgz#c0a9a0e679910853add32c28bc5b1f47e2059ba4" - integrity sha512-rzHBONkRnIkk/QWZCi0pMPhxgioXrEoMlASCCCLF65RuO4IU0Wg1YvQJdNOMG1GmX1ewTkXuYPEMeHcHXTWO4w== +"@ionic-native/background-mode@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/background-mode/-/background-mode-5.15.0.tgz#5f1e5a36cd5e95e2827c14bc8dc5f3c408c315a2" + integrity sha512-/ktXUaYtaHFN++0R8yODeC8IMOMbI9Gk7NQAyHor72GcNhXfgKzY9w2RrAhhD/ZvyXWkL7EPJP6nC7FFUuizwQ== dependencies: "@types/cordova" latest -"@ionic-native/barcode-scanner@^5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/barcode-scanner/-/barcode-scanner-5.14.0.tgz#f1ba172d0cd5b8278838348cf7896ffafc0b95d4" - integrity sha512-FwTHQD5TUaR441V76IoXQmrSdz7AZ/qBfrfh/rDaXWgYHg7iJA3prOlBPCW+YGfLV3qvlRJeq/ASub/JkTBDrg== +"@ionic-native/barcode-scanner@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/barcode-scanner/-/barcode-scanner-5.15.0.tgz#fbd588a41083d4ae6b8449c55de7ee96c6f0f499" + integrity sha512-l3hTi1w7ITJvJs+uNSbhJ8vZR6+P1UHxdgj5PL1xd/+CUTAzlIQIrivUHLdjfdjjxBGizh54rakr3HeKxV5SNg== dependencies: "@types/cordova" latest -"@ionic-native/core@^5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/core/-/core-5.14.0.tgz#98653a597f6008d33098062e03ed045addd9e2ba" - integrity sha512-kCqy4OxT/QvGmgB7ha9gnZivjURxiEh5+bd9O8PRI7CC8+2ymT0bRUBZB+ZYQSZ6AsXVwJ2xIelf2drK+vXCpQ== +"@ionic-native/core@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/core/-/core-5.15.0.tgz#f7674bbfd9a197677b4c92407f56c1c51ed1a8ef" + integrity sha512-CBZdcud/VY58rKPhnLvWm+M6vt0SCynRmyHvfnV2H/2dzwpVbAQUVm32eYAfCBzV1BCX4Yyrf3Qzic2nd9MhUw== dependencies: "@types/cordova" latest -"@ionic-native/device@^5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/device/-/device-5.14.0.tgz#e1a6b1e3459db570fa11f2d956cbfafe4f8b080f" - integrity sha512-9BrZYlsMsM2JnfLip06nhzWJ3d7KRMqJdwZOOce+BzKdtTTQ++7RF3GSWjPiu5El+fArgN8deoQuuc8Qts6Rbg== +"@ionic-native/device@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/device/-/device-5.15.0.tgz#6b794eefee8e79900f37a5a61a1ceb914f35f426" + integrity sha512-RQYVq99ELsFzgt+T3FmMdPi/eTODBmzadkqFuPYNYygjXU2yj8JSuflmGTb+R252iME0IcwWYpMv3cG3XYVNmw== dependencies: "@types/cordova" latest -"@ionic-native/dialogs@^5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/dialogs/-/dialogs-5.14.0.tgz#740e5effd1b3fff4751c06d9666e37a33a979623" - integrity sha512-889emiOcW+MBuk3rP3KzJ9m34+77cs8YgXMPhSm66ozxK7r/0+0YAMfHjRj9KR9bQF6rQu41BsFIFdxJfW/qFw== +"@ionic-native/dialogs@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/dialogs/-/dialogs-5.15.0.tgz#722270641eebb685e6bb926f8ce4ebaab3f53bdc" + integrity sha512-1tU3+R7KZhB6T1vswX2dgvFXp1uzmoJkOykUJJSpabNC7Stav5RKG210dO3bWvNWBuP8c9ZJdCBDFPbnYrlfqw== dependencies: "@types/cordova" latest -"@ionic-native/file@^5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/file/-/file-5.14.0.tgz#71300c0ca1c77f2c74a90e8e35f632e108782097" - integrity sha512-r4o7wk7UpvEu7iIyD1Rbd8D4Nio5yyTgZu5ztQy+x53zMqfOPKp/63SZPsFZGRd3RsgkSchucGqrbip8m6K7tw== +"@ionic-native/file@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/file/-/file-5.15.0.tgz#f88eaaca70c30f91dfae75404247f9f6ca521408" + integrity sha512-oNJFITc4dnKCGF41QLHfA3huDADew6P4OSoCWuj3Iroj7c1Ls6Tf0yP75P+bzArUdCdUvfjNtLeY4hywt5B2Tg== dependencies: "@types/cordova" latest -"@ionic-native/firebase@^5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/firebase/-/firebase-5.14.0.tgz#a8f36211e4d7427b7234be1c901d273bf44fbd39" - integrity sha512-Pnz9sXhc01EmM5QwA/2eYKIBnf5rKSMUcIGM7YXR1H9AbEz6IkgNGLqHD2QY5tD8csFzDDJomGKbBiY16fLsHg== +"@ionic-native/firebase@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/firebase/-/firebase-5.15.0.tgz#09c67130bfae5d2c0fc489b17da038f662310c05" + integrity sha512-illYr8KP8mTduHSo1QrdgCV7WBHTVvxEQEphcJj47E+3PUHhzBAiNhTmxUf69jG2ghhHNQaonnvKPTbaAMYnGw== dependencies: "@types/cordova" latest -"@ionic-native/globalization@^5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/globalization/-/globalization-5.14.0.tgz#c22d873662937faa3efd5bc6531bd13971ed41e7" - integrity sha512-kR5DPVsleTSAcaIzrhhFlrd4kf7PoFvXfnFErOXTxwc/96oiD4HxyD4k/5teCWvP81gCEzWXGizqaMAmOTsPRQ== +"@ionic-native/globalization@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/globalization/-/globalization-5.15.0.tgz#c36a641d004cecd1fae280bb4cbf46477b163fc4" + integrity sha512-piR+DY+VnoYdIPLwvtZFolUV/rHZz317XCbIr8YIW8EwDGUjGRFqkmNNWTXgDezo8MhoGaMkTOydDUN4MnUy5Q== dependencies: "@types/cordova" latest -"@ionic-native/insomnia@^5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/insomnia/-/insomnia-5.14.0.tgz#fda734739c9c79ec55fa16665e6c3c2b78d88d66" - integrity sha512-WIzibu0Zi7kU1RFgD2QJweF/BgLrcsTxgkgRPtjeEReYQcrLQx2b7u4Or/UWPgAgLdt9lWU0k1iV+VOK08+D5g== +"@ionic-native/insomnia@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/insomnia/-/insomnia-5.15.0.tgz#93ffdea5252bb87d22064488b48896e9764d100b" + integrity sha512-QrSlRUXe63Dmsygzqzi9anuHxccHfEbWdiRWqD/p7fsVskYCRQGWd1wYZSAF7l5Sw2T4ze76LDx55kw3V2URig== dependencies: "@types/cordova" latest -"@ionic-native/keyboard@^5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/keyboard/-/keyboard-5.14.0.tgz#fcb069c26e92fb305fdd1a2f7cea5b81ff03caf3" - integrity sha512-NEtdzLA2Mo4hQyF5H+Itg0sL3e5b3T+rU0Fq/ZRUO+ndliyKaisGrWf1a6Dxf8Vwokbtf3d3ukep6TmaMvJNQw== +"@ionic-native/keyboard@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/keyboard/-/keyboard-5.15.0.tgz#277975fcd85182ca22249c28482d2d67cfb47479" + integrity sha512-EIhD0erJqEeblyhgHYZ6LGN7ISDTsdqTh+ydczNW6jXfiJHj+3/QceuYfwwLBD/nlql2RbqlqWjr12BVP2E4Og== dependencies: "@types/cordova" latest -"@ionic-native/local-notifications@^5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/local-notifications/-/local-notifications-5.14.0.tgz#f0989ad59bb44c10ea0a8e87fdbef0665589a1bd" - integrity sha512-daCRc8dsFbGeX8gxs/I7+Zpemv5lC8xGx/Clr62Z3/zrnbFRzbJxKoJSXz9ge41HJzfitWSy2LeoFdlGrs9ZQQ== +"@ionic-native/local-notifications@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/local-notifications/-/local-notifications-5.15.0.tgz#f3e3167c481bc1d245dc88d939baf9403f13d027" + integrity sha512-aViXL/fxuuh8DjTPMDFSaPAI4oAPL6QFkdM8h/FuCokLyDy6P3aR4kn7aoSU30/WunjFY5MaMs1PIVXcsMU+Nw== dependencies: "@types/cordova" latest -"@ionic-native/mobile-accessibility@5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/mobile-accessibility/-/mobile-accessibility-5.14.0.tgz#6a4bc9d7437c8ac28731cef1a97dd2e3c75782a5" - integrity sha512-6ykSf3p5qOwFNL9px6XbuoJXHOGFbV/WBrNUwKWpxDnmUith5FyBMDQygnCAXamN75AoNvocYA5Gen18ZQjkxQ== +"@ionic-native/mobile-accessibility@5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/mobile-accessibility/-/mobile-accessibility-5.15.0.tgz#c2c946312fd76131fe40eec74078422a5813ef68" + integrity sha512-miwnk8bxjyH8iEUrWm2diDVT9/QCBXG5PF8c5DDY0rVkpkI7UYYflSaBDXQsvSoyunu1Oq8I3C8yOsgmsRIlkQ== dependencies: "@types/cordova" latest -"@ionic-native/splash-screen@^5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/splash-screen/-/splash-screen-5.14.0.tgz#25450ea6034aed183dff87e14553bfc6c00f349f" - integrity sha512-HUbTMXbebFWk/XSZGDbL3ztytsM9OEZmiLLAVbp9rrwVaENqpoXS7FbBAV2BqKbSBqEMHjxby8JCJFWTOhiBdg== +"@ionic-native/splash-screen@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/splash-screen/-/splash-screen-5.15.0.tgz#34624f272cc7654977883a427aa3f5653e26032e" + integrity sha512-nq6/JBUeWhCnaduf0hYfy7w11FhAdeei6tgAPJdVKdTmNYhU9F06tKdtS7dzkt9yUilDPqB2afWvrlUKFKsWFw== dependencies: "@types/cordova" latest -"@ionic-native/status-bar@^5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/status-bar/-/status-bar-5.14.0.tgz#410e009d9dd8ddb2992f6f27115b2ef1feb868fa" - integrity sha512-qmXwIlO1/1PFZTMpKXc9UZQiwy8gXyhuDE/f/FwQ0LLar7Wt611rU3RrUWmPL6UBuOAeAm98DghgI9RcoQ8s+Q== +"@ionic-native/status-bar@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/status-bar/-/status-bar-5.15.0.tgz#3ba29fb549886f797aee0eefcffc4be00b5c7a2d" + integrity sha512-K4Vm20UB5kBfOSYPd51QBj/9zGIN1saaW0ZcM5YHlXb2GMs4Z2UUg7kGZ16wL3XCx2WkPhUk5+bjuWfbui0Mmg== dependencies: "@types/cordova" latest -"@ionic-native/vibration@^5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/vibration/-/vibration-5.14.0.tgz#a8668a25c3359f8b9421af27164a00c01bd4d999" - integrity sha512-GLrm1RXCf/JVXfEQAMIBZWC9NikyNb5xWsmIHh3X8zkYkFDjYyqMCdW+dt2+eOfBdJCYMiJ4n5k5lZq5pUF0AA== +"@ionic-native/vibration@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/vibration/-/vibration-5.15.0.tgz#fc1444186f486d6ec85483ee65f3c2ede357490f" + integrity sha512-mfWKF1bIakJKZMHEL9QWGqho7FVsQNVk6N79TjpLmNkt868XDkH6wtlLnGQLhmbxG+4+rm4FL4FL8C3q4Y+mYA== dependencies: "@types/cordova" latest -"@ionic-native/web-intent@^5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@ionic-native/web-intent/-/web-intent-5.14.0.tgz#5dc76f0ac1b96133c75bd433f584f8a80a508291" - integrity sha512-e7YDUDiMNoBypvq0+G2Myd9rLXMxDgXMh/ZzbuLIqp/uKN9PqtlSYtVw3jQO1FKSMgauXgL/acQ+I2JTlTesbQ== +"@ionic-native/web-intent@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@ionic-native/web-intent/-/web-intent-5.15.0.tgz#912f3a4711bf7c327154e206770ccf94fb3c8305" + integrity sha512-s4CQQ/OnJR6BFQOR2elzOGvG7bxPFdsqioEVc01CBfkbZY3l3VBj4AaLnAh1YBELKFkO+tKwgIdZYfg+zTNQxw== dependencies: "@types/cordova" latest @@ -705,15 +705,17 @@ "@types/minimatch" "*" "@types/node" "*" -"@types/jasmine@^3.4.1": - version "3.4.1" - resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.4.1.tgz#214496efdd7870f133051a95d1692f9cc0730ee7" - integrity sha512-kA2/srq6lb/kiczw+l/yW7lKdAN9Ae5A/pp1bhu8RBzhYY/ybc1hwav3sWrPyAwSRcAS95NVB0uZdM62qT75EA== +"@types/jasmine@*", "@types/jasmine@^3.4.4": + version "3.4.4" + resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.4.4.tgz#be3fbd73e72725edb44e6f7f509cd52912d1550c" + integrity sha512-+/sHcTPyDS1JQacDRRRWb+vNrjBwnD+cKvTaWlxlJ/uOOFvzCkjOwNaqVjYMLfsjzNi0WtDH9RyReDXPG1Cdug== -"@types/jasminewd2@^2.0.7": - version "2.0.7" - resolved "https://registry.yarnpkg.com/@types/jasminewd2/-/jasminewd2-2.0.7.tgz#71756ac7780add2dcd32384919ac903ad550f643" - integrity sha512-pKeZFRn4vQyd8Px9FK6ww3frKiAYPgpKzBayWHd9SOzGjty6aXPsuFvxgRr866KLP1h7Cnd1z3XrhGX17WqO2g== +"@types/jasminewd2@^2.0.8": + version "2.0.8" + resolved "https://registry.yarnpkg.com/@types/jasminewd2/-/jasminewd2-2.0.8.tgz#67afe5098d5ef2386073a7b7384b69a840dfe93b" + integrity sha512-d9p31r7Nxk0ZH0U39PTH0hiDlJ+qNVGjlt1ucOoTUptxb2v+Y5VMnsxfwN+i3hK4yQnqBi3FMmoMFcd1JHDxdg== + dependencies: + "@types/jasmine" "*" "@types/minimatch@*": version "3.0.3" @@ -1591,23 +1593,23 @@ autoprefixer@^7.2.6: postcss "^6.0.17" postcss-value-parser "^3.2.3" -autoprefixer@^9.5.1, autoprefixer@^9.6.1: - version "9.6.1" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.6.1.tgz#51967a02d2d2300bb01866c1611ec8348d355a47" - integrity sha512-aVo5WxR3VyvyJxcJC3h4FKfwCQvQWb1tSI5VHNibddCVWrcD1NvlxEweg3TSgiPztMnWfjpy2FURKA2kvDE+Tw== +autoprefixer@^9.5.1, autoprefixer@^9.6.4: + version "9.6.4" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.6.4.tgz#e6453be47af316b2923eaeaed87860f52ad4b7eb" + integrity sha512-Koz2cJU9dKOxG8P1f8uVaBntOv9lP4yz9ffWvWaicv9gHBPhpQB22nGijwd8gqW9CNT+UdkbQOQNLVI8jN1ZfQ== dependencies: - browserslist "^4.6.3" - caniuse-lite "^1.0.30000980" + browserslist "^4.7.0" + caniuse-lite "^1.0.30000998" chalk "^2.4.2" normalize-range "^0.1.2" num2fraction "^1.2.2" - postcss "^7.0.17" - postcss-value-parser "^4.0.0" + postcss "^7.0.18" + postcss-value-parser "^4.0.2" -avsc@^5.4.13: - version "5.4.13" - resolved "https://registry.yarnpkg.com/avsc/-/avsc-5.4.13.tgz#2e55d92e461eb9634019a28df511cc60dd19f8a7" - integrity sha512-OLS+OPfvBIJA+Vn4JpjDiMt4UNvw0v54TBKp3oR0YvnDyjBqAdm3V2t/SfdUk6rwUZxBPDoykOG59lXMxNllbg== +avsc@^5.4.14: + version "5.4.14" + resolved "https://registry.yarnpkg.com/avsc/-/avsc-5.4.14.tgz#8e3751d98c6a7964882e953faa10716b93bcfe5b" + integrity sha512-9RjU+v50FG4Wdpo7Tm6eQ2xrwaYiwYf9oXkYMMPlI5g4hqGkhJWLmBy8wdxXsglbDQfJ6sefrVY5DWWI7aapoQ== aws-sign2@~0.7.0: version "0.7.0" @@ -2069,14 +2071,14 @@ browserslist@^2.11.3: caniuse-lite "^1.0.30000792" electron-to-chromium "^1.3.30" -browserslist@^4.4.1, browserslist@^4.6.3: - version "4.6.6" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.6.tgz#6e4bf467cde520bc9dbdf3747dafa03531cec453" - integrity sha512-D2Nk3W9JL9Fp/gIcWei8LrERCS+eXu9AM5cfXA8WEZ84lFks+ARnZ0q/R69m2SV3Wjma83QDDPxsNKXUwdIsyA== +browserslist@^4.4.1, browserslist@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.7.0.tgz#9ee89225ffc07db03409f2fee524dc8227458a17" + integrity sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA== dependencies: - caniuse-lite "^1.0.30000984" - electron-to-chromium "^1.3.191" - node-releases "^1.1.25" + caniuse-lite "^1.0.30000989" + electron-to-chromium "^1.3.247" + node-releases "^1.1.29" browserstack-local@^1.3.7: version "1.4.2" @@ -2341,15 +2343,10 @@ caniuse-db@^1.0.30000187, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000988.tgz#6a761a0204c4c41977b0379fb14f51d367c5e4ee" integrity sha512-P0JCbGJhL1tTTeF+ehckvXwtckRursLDbA/ETdAd8Yg1ROAGJ5OgYgMONW1zWW1+ZTB79d7ZeJiOHGreEGG1ew== -caniuse-lite@^1.0.30000792, caniuse-lite@^1.0.30000805, caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30000984: - version "1.0.30000988" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000988.tgz#742f35ec1b8b75b9628d705d7652eea1fef983db" - integrity sha512-lPj3T8poYrRc/bniW5SQPND3GRtSrQdUM/R4mCYTbZxyi3jQiggLvZH4+BYUuX0t4TXjU+vMM7KFDQg+rSzZUQ== - -caniuse-lite@^1.0.30000929: - version "1.0.30000989" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000989.tgz#b9193e293ccf7e4426c5245134b8f2a56c0ac4b9" - integrity sha512-vrMcvSuMz16YY6GSVZ0dWDTJP8jqk3iFQ/Aq5iqblPwxSVVZI+zxDyTX0VPqtQsDnfdrBDcsmhgTEOh5R8Lbpw== +caniuse-lite@^1.0.30000792, caniuse-lite@^1.0.30000805, caniuse-lite@^1.0.30000929, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30000998: + version "1.0.30000999" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000999.tgz#427253a69ad7bea4aa8d8345687b8eec51ca0e43" + integrity sha512-1CUyKyecPeksKwXZvYw0tEoaMCo/RwBlXmEtN5vVnabvO0KPd9RQLcaAuR9/1F+KDMv6esmOFWlsXuzDk+8rxg== canonical-path@1.0.0: version "1.0.0" @@ -3117,9 +3114,10 @@ cordova-plugin-local-notification@^0.9.0-beta.3: resolved "https://registry.yarnpkg.com/cordova-plugin-local-notification/-/cordova-plugin-local-notification-0.9.0-beta.3.tgz#f63d49b90ea9cdf1a5de53e7eb478a09bb236f93" integrity sha512-L3Z1velxrkm9nHFcvLnMgBPZjKFt6hwM6hn1lA+JFwIR26Yw6UF72z+/lRMBclAcOxBIDYCqeaLgvezmajjuEg== -"cordova-plugin-network-information@git+https://github.com/apache/cordova-plugin-network-information.git": - version "2.0.3-dev" - resolved "git+https://github.com/apache/cordova-plugin-network-information.git#97a4967a2e0f0324db4a6d789477ec5f761f2770" +cordova-plugin-network-information@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/cordova-plugin-network-information/-/cordova-plugin-network-information-2.0.2.tgz#f48a92cbd7cccb63cb0a5e5a00030001b8ac4fea" + integrity sha512-NwO3qDBNL/vJxUxBTPNOA1HvkDf9eTeGH8JSZiwy1jq2W2mJKQEDBwqWkaEQS19Yd/MQTiw0cykxg5D7u4J6cQ== cordova-plugin-splashscreen@^5.0.3: version "5.0.3" @@ -3874,11 +3872,16 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.191, electron-to-chromium@^1.3.30: +electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.30: version "1.3.211" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.211.tgz#0c39d17316bf342d1971fed68e754fa1775918f7" integrity sha512-GZAiK3oHrs0K+LwH+HD+bdjZ17v40oQQdXbbd3dgrwgbENvazrGpcuIADSAREWnxzo9gADB1evuizrbXsnoU2Q== +electron-to-chromium@^1.3.247: + version "1.3.278" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.278.tgz#04c8f96382395aca225bc340be6b9dbbe2bf50eb" + integrity sha512-4cPkOCY5k4z69MHOA96VUt+Wl24AbLHQcm7W9ckabJ/iRe7oBFNMiliw75lK/w++R9bKCUxJ0mFnMRMylnAlbA== + elementtree@0.1.7, elementtree@^0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/elementtree/-/elementtree-0.1.7.tgz#9ac91be6e52fb6e6244c4e54a4ac3ed8ae8e29c0" @@ -5937,10 +5940,10 @@ ionic-mocks@^1.3.0: resolved "https://registry.yarnpkg.com/ionic-mocks/-/ionic-mocks-1.3.0.tgz#398bae25e01f23ab31b6d379754ee9d3c4bfb066" integrity sha1-OYuuJeAfI6sxttN5dU7p08S/sGY= -ionic@^5.4.1: - version "5.4.1" - resolved "https://registry.yarnpkg.com/ionic/-/ionic-5.4.1.tgz#94923038e14128b95aed44be4c22badd95d4cc15" - integrity sha512-z4Zdb1lEZoqx6HsURVgdqGw0INtDeNrbahTZacrqm3cNmjq6X4SOP+dILIeZoURq/V0KcyVcVQnssWRFbVd8TQ== +ionic@^5.4.2: + version "5.4.2" + resolved "https://registry.yarnpkg.com/ionic/-/ionic-5.4.2.tgz#ad951f4f7df3d7337feb2f5228a4066f686296da" + integrity sha512-VEbpHPvoMSNC1fR1/5YkZx6JwkvpjYq9EuEyrEphcPWZ8qal8ijZPJnQHhXzVI3u5c5oR8BdlniePdmtCxnsVA== dependencies: "@ionic/cli-framework" "2.1.7" "@ionic/discover" "2.0.6" @@ -6856,10 +6859,10 @@ kind-of@^6.0.0, kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== -known-css-properties@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.15.0.tgz#5aa14a98b5a1652448aad44a1c67e867e4e7d88c" - integrity sha512-TS0RCcQfHYsA+59uIHhnsA71NBkpILbqi0W+hde4R5FtESdzur0tCJFoko/1Pbhx+8rmdUc0R1VE4ixnnD+9xw== +known-css-properties@^0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.16.0.tgz#3f0597214db11a460df77cd44bcb39e263b9da6c" + integrity sha512-0g5vDDPvNnQk7WM/aE92dTDxXJoOE0biiIcUb3qkn/F6h/ZQZPlZIbE2XSXH2vFPfphkgCxuR2vH6HHnobEOaQ== known-css-properties@^0.2.0: version "0.2.0" @@ -7895,12 +7898,12 @@ node-pre-gyp@^0.12.0: semver "^5.3.0" tar "^4" -node-releases@^1.1.25: - version "1.1.26" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.26.tgz#f30563edc5c7dc20cf524cc8652ffa7be0762937" - integrity sha512-fZPsuhhUHMTlfkhDLGtfY80DSJTjOcx+qD1j5pqPkuhUHVS7xHZIg9EE4DHK8O3f0zTxXHX5VIkDG8pu98/wfQ== +node-releases@^1.1.29: + version "1.1.35" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.35.tgz#32a74a3cd497aa77f23d509f483475fd160e4c48" + integrity sha512-JGcM/wndCN/2elJlU0IGdVEJQQnJwsLbgPCFd2pY7V0mxf17bZ0Gb/lgOtL29ZQhvEX5shnVhxQyZz3ex94N8w== dependencies: - semver "^5.3.0" + semver "^6.3.0" node-sass@4.12.0, node-sass@^4.10.0, node-sass@^4.12.0: version "4.12.0" @@ -8909,7 +8912,7 @@ postcss-value-parser@^3.1.1, postcss-value-parser@^3.2.3, postcss-value-parser@^ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== -postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2: +postcss-value-parser@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz#482282c09a42706d1fc9a069b73f44ec08391dc9" integrity sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ== @@ -9964,7 +9967,7 @@ semver@5.6.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== -semver@^6.0.0, semver@^6.1.2: +semver@^6.0.0, semver@^6.1.2, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -10870,10 +10873,10 @@ stylelint-order@^0.4.4: postcss "^5.2.16" stylelint "^7.9.0" -stylelint@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-11.0.0.tgz#1458d1e126d4f2fb9f41076197f852aa1fcae91d" - integrity sha512-esKkG7CUXI5yr4jgCNuwjiiV6NJ4BpodB0e47oFvUBaHgpiXXHRPOajpb0IXL7Ucpk+X3dcrlPxVHpmJ5XUDwg== +stylelint@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-11.1.1.tgz#6dbbb348036576ac6b033ddfedba72a877c1d6bb" + integrity sha512-Vx6TAJsxG6qksiFvxQTKriQhp1CqUWdpTDITEkAjTR+l+8Af7qNlvrUDXfpuFJgXh/ayF8xdMSKE+SstcsPmMA== dependencies: autoprefixer "^9.5.1" balanced-match "^1.0.0" @@ -10890,7 +10893,7 @@ stylelint@^11.0.0: ignore "^5.0.6" import-lazy "^4.0.0" imurmurhash "^0.1.4" - known-css-properties "^0.15.0" + known-css-properties "^0.16.0" leven "^3.1.0" lodash "^4.17.14" log-symbols "^3.0.0" @@ -12113,10 +12116,10 @@ webpack@4.29.0: watchpack "^1.5.0" webpack-sources "^1.3.0" -webpack@^4.41.0: - version "4.41.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.0.tgz#db6a254bde671769f7c14e90a1a55e73602fc70b" - integrity sha512-yNV98U4r7wX1VJAj5kyMsu36T8RPPQntcb5fJLOsMz/pt/WrKC0Vp1bAlqPLkA1LegSwQwf6P+kAbyhRKVQ72g== +webpack@^4.41.1: + version "4.41.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.1.tgz#5388dd3047d680d5d382a84249fd4750e87372fd" + integrity sha512-ak7u4tUu/U63sCVxA571IuPZO/Q0pZ9cEXKg+R/woxkDzVovq57uB6L2Hlg/pC8LCU+TWpvtcYwsstivQwMJmw== dependencies: "@webassemblyjs/ast" "1.8.5" "@webassemblyjs/helper-module-context" "1.8.5" @@ -12406,10 +12409,10 @@ yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== -yaml@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.7.0.tgz#b4cddb83490051e6c4b6ffe2bb08221c23f4c6cf" - integrity sha512-BEXCJKbbJmDzjuG4At0R4nHJKlP81hxoLQqUCaxzqZ8HHgjAlOXbiOHVCQ4YuQqO/rLR8HoQ6kxGkYJ3tlKIsg== +yaml@^1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.7.1.tgz#86db1b39a6434a7928d3750cbe08303a50a24d6b" + integrity sha512-sR0mJ2C3LVBgMST+0zrrrsKSijbw64bfHmTt4nEXLZTZFyIAuUVARqA/LO5kaZav2OVQMaZ+5WRrZv7QjxG3uQ== dependencies: "@babel/runtime" "^7.5.5"