diff --git a/android/app/build.gradle b/android/app/build.gradle index f9dcd757..f7683ec7 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -6,7 +6,7 @@ android { applicationId "de.reedu.origaminext" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 1 + versionCode 3 versionName "0.21" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/android/app/src/main/res/xml/config.xml b/android/app/src/main/res/xml/config.xml index 49402728..01d76944 100644 --- a/android/app/src/main/res/xml/config.xml +++ b/android/app/src/main/res/xml/config.xml @@ -6,14 +6,5 @@ - - - - - - - - - \ No newline at end of file diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index 10050182..375d07ca 100644 --- a/ios/App/App.xcodeproj/project.pbxproj +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -357,7 +357,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = App/App.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 30; + CURRENT_PROJECT_VERSION = 31; DEVELOPMENT_TEAM = 7D2772F462; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -379,7 +379,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = App/App.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 30; + CURRENT_PROJECT_VERSION = 31; DEVELOPMENT_TEAM = 7D2772F462; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; diff --git a/ios/App/App/config.xml b/ios/App/App/config.xml index f9e52af1..a53fe336 100644 --- a/ios/App/App/config.xml +++ b/ios/App/App/config.xml @@ -6,14 +6,5 @@ - - - - - - - - - \ No newline at end of file diff --git a/package.json b/package.json index 4471c42e..60894105 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "@capacitor/ios": "^2.4.2", "@ionic-native/core": "^5.24.0", "@ionic-native/device-orientation": "^5.26.0", - "@ionic-native/file-transfer": "^5.24.0", "@ionic/angular": "^5.0.7", "@ionic/pwa-elements": "^3.0.1", "@ionic/storage": "^2.2.0", @@ -51,8 +50,6 @@ "cordova-browser": "6.0.0", "cordova-ios": "^5.1.0", "cordova-plugin-device-orientation": "^2.0.1", - "cordova-plugin-file": "^6.0.2", - "cordova-plugin-file-transfer": "^1.7.1", "core-js": "^3.6.5", "es6-promise-plugin": "^4.2.2", "jetifier": "^1.6.5", @@ -102,9 +99,7 @@ "description": "An Ionic project", "cordova": { "plugins": { - "cordova-plugin-device-orientation": {}, - "cordova-plugin-file": {}, - "cordova-plugin-file-transfer": {} + "cordova-plugin-device-orientation": {} }, "platforms": [ "android", @@ -120,4 +115,4 @@ "path": false, "os": false } -} \ No newline at end of file +} diff --git a/src/app/components/feedback/feedback.component.ts b/src/app/components/feedback/feedback.component.ts index c79dc0b2..f5ebb95d 100644 --- a/src/app/components/feedback/feedback.component.ts +++ b/src/app/components/feedback/feedback.component.ts @@ -10,10 +10,8 @@ import booleanPointInPolygon from "@turf/boolean-point-in-polygon"; import { TrackerService } from '../../services/tracker.service'; import { Component } from '@angular/core'; import { Plugins, Capacitor, GeolocationPosition } from '@capacitor/core'; -import { DeviceOrientationCompassHeading, DeviceOrientation } from '@ionic-native/device-orientation/ngx'; -import { env } from 'process'; -import { environment } from 'src/environments/environment'; import centroid from '@turf/centroid' +import { OrigamiOrientationService } from 'src/app/services/origami-orientation.service'; enum FeedbackType { @@ -56,22 +54,20 @@ export class FeedbackComponent { private feedbackDuration: number = 2000; deviceOrientationSubscription: any; - private deviceOrientation: DeviceOrientation; private direction: number; successColor: string; private DIRECTION_TRESHOLD: number = 22.5 - constructor() { } + constructor(private orientationService: OrigamiOrientationService) { } - init(map: any, geolocationService: OrigamiGeolocationService, deviceOrientation: DeviceOrientation, helperService: HelperService, toastController: ToastController, trackerService: TrackerService, playingGamePage: PlayingGamePage) { + init(map: any, geolocationService: OrigamiGeolocationService, helperService: HelperService, toastController: ToastController, trackerService: TrackerService, playingGamePage: PlayingGamePage) { this.map = map this.geolocationService = geolocationService this.helperService = helperService this.toastController = toastController this.trackerService = trackerService this.playingGamePage = playingGamePage - this.deviceOrientation = deviceOrientation this.successColor = getComputedStyle(document.documentElement).getPropertyValue('--ion-color-success'); @@ -109,14 +105,9 @@ export class FeedbackComponent { } } }); - - this.deviceOrientationSubscription = this.deviceOrientation - .watchHeading({ - frequency: 300 - }) - .subscribe((data: DeviceOrientationCompassHeading) => { - this.direction = data.magneticHeading - }) + this.deviceOrientationSubscription = this.orientationService.orientationSubscription.subscribe((heading: number) => { + this.direction = heading + }) } public setTask(task: Task) { diff --git a/src/app/mapControllers/layer-control.ts b/src/app/mapControllers/layer-control.ts index d3c37ac4..c8e9b2d8 100644 --- a/src/app/mapControllers/layer-control.ts +++ b/src/app/mapControllers/layer-control.ts @@ -1,14 +1,9 @@ import { Marker, Map as MapboxMap } from "mapbox-gl"; import { MapboxStyleSwitcherControl } from "mapbox-gl-style-switcher"; import MapboxCompare from "mapbox-gl-compare"; -import { ElementRef, Injectable, ViewChild } from '@angular/core'; -import { - DeviceOrientation, - DeviceOrientationCompassHeading -} from "@ionic-native/device-orientation/ngx"; -import { Subscription, Observable, Subscriber, fromEvent } from 'rxjs'; +import { ElementRef } from '@angular/core'; +import { Subscription, Observable, fromEvent } from 'rxjs'; import { AlertController, Platform } from '@ionic/angular'; -import { shareReplay } from 'rxjs/operators'; export enum LayerType { @@ -48,7 +43,7 @@ export class LayerControl { public swipeClickSubscription: Observable = null; - constructor(map: MapboxMap, mapWrapper: ElementRef, private deviceOrientation: DeviceOrientation, alertController: AlertController, platform: Platform) { + constructor(map: MapboxMap, mapWrapper: ElementRef, alertController: AlertController, platform: Platform) { this.map = map; this.alertController = alertController; this.platform = platform; diff --git a/src/app/mapControllers/rotation-control.ts b/src/app/mapControllers/rotation-control.ts index c92d4d90..baffad3f 100644 --- a/src/app/mapControllers/rotation-control.ts +++ b/src/app/mapControllers/rotation-control.ts @@ -1,9 +1,6 @@ -import { IControl, Map as MapboxMap } from "mapbox-gl"; -import { - DeviceOrientation, - DeviceOrientationCompassHeading -} from "@ionic-native/device-orientation/ngx"; +import { Map as MapboxMap } from "mapbox-gl"; import { Subscription } from 'rxjs'; +import { OrigamiOrientationService } from '../services/origami-orientation.service'; export enum RotationType { Manual, @@ -14,14 +11,11 @@ export enum RotationType { export class RotationControl { private deviceOrientationSubscription: Subscription; - private deviceOrientation: DeviceOrientation private rotationType: RotationType = RotationType.Manual private map: MapboxMap; - constructor(map: MapboxMap) { - this.deviceOrientation = new DeviceOrientation() - + constructor(map: MapboxMap, private orientationService: OrigamiOrientationService) { this.map = map; this.rotate(); } @@ -56,13 +50,12 @@ export class RotationControl { case RotationType.Auto: this.map.dragRotate.disable(); this.map.touchZoomRotate.disableRotation(); - this.deviceOrientationSubscription = this.deviceOrientation - .watchHeading({ frequency: 10 }) - .subscribe((data: DeviceOrientationCompassHeading) => { - requestAnimationFrame(() => { - this.map.rotateTo(data.magneticHeading, { duration: 20 }); - }) + + this.deviceOrientationSubscription = this.orientationService.orientationSubscription.subscribe((heading: number) => { + requestAnimationFrame(() => { + this.map.rotateTo(heading, { duration: 20 }); }) + }) break; case RotationType.Button: diff --git a/src/app/mapControllers/view-direction-control.ts b/src/app/mapControllers/view-direction-control.ts index 4a15d93c..34835f14 100644 --- a/src/app/mapControllers/view-direction-control.ts +++ b/src/app/mapControllers/view-direction-control.ts @@ -1,10 +1,7 @@ import { Map as MapboxMap } from "mapbox-gl"; -import { - DeviceOrientation, - DeviceOrientationCompassHeading -} from "@ionic-native/device-orientation/ngx"; import { Subscription } from 'rxjs'; import { OrigamiGeolocationService } from '../services/origami-geolocation.service'; +import { OrigamiOrientationService } from '../services/origami-orientation.service'; export enum ViewDirectionType { None, @@ -15,7 +12,6 @@ export enum ViewDirectionType { export class ViewDirectionControl { private deviceOrientationSubscription: Subscription; - private deviceOrientation: DeviceOrientation private positionSubscription: Subscription; private viewDirectionType: ViewDirectionType = ViewDirectionType.None @@ -23,11 +19,9 @@ export class ViewDirectionControl { private isInitalized = false; - constructor(map: MapboxMap, deviceOrientation: DeviceOrientation, private geolocationService: OrigamiGeolocationService) { + constructor(map: MapboxMap, private geolocationService: OrigamiGeolocationService, private orientationService: OrigamiOrientationService) { this.map = map; - this.deviceOrientation = deviceOrientation; - this.positionSubscription = this.geolocationService.geolocationSubscription.subscribe( position => { if (this.map != undefined && this.isInitalized) { @@ -38,17 +32,14 @@ export class ViewDirectionControl { } } ); - this.deviceOrientationSubscription = this.deviceOrientation - .watchHeading({ - frequency: 300 - }) - .subscribe((data: DeviceOrientationCompassHeading) => { - this.map.setLayoutProperty( - "viewDirection", - "icon-rotate", - data.magneticHeading - this.map.getBearing() - ); - }) + this.deviceOrientationSubscription = this.orientationService.orientationSubscription.subscribe((heading: number) => { + this.map.setLayoutProperty( + "viewDirection", + "icon-rotate", + heading - this.map.getBearing() + ); + }) + this.map.loadImage( "/assets/icons/directionv2.png", (error, image) => { @@ -137,6 +128,5 @@ export class ViewDirectionControl { if (this.deviceOrientationSubscription != undefined) { this.deviceOrientationSubscription.unsubscribe(); } - // navigator.geolocation.clearWatch(this.positionWatch); } } \ No newline at end of file diff --git a/src/app/pages/create-game/create-game-list/create-game-list.module.ts b/src/app/pages/create-game/create-game-list/create-game-list.module.ts index 2432d521..c42c96e5 100644 --- a/src/app/pages/create-game/create-game-list/create-game-list.module.ts +++ b/src/app/pages/create-game/create-game-list/create-game-list.module.ts @@ -7,7 +7,6 @@ import { IonicModule } from '@ionic/angular'; import { CreateGameListPage } from './create-game-list.page'; -import { FileTransfer } from '@ionic-native/file-transfer/ngx'; const routes: Routes = [ { @@ -24,9 +23,6 @@ const routes: Routes = [ RouterModule.forChild(routes) ], declarations: [CreateGameListPage], - providers: [ - FileTransfer, - // WebView - ] + providers: [] }) export class CreateGameListPageModule { } diff --git a/src/app/pages/create-game/create-info-modal/create-info-modal.component.html b/src/app/pages/create-game/create-info-modal/create-info-modal.component.html index a8d59f5e..2fc94ac5 100644 --- a/src/app/pages/create-game/create-info-modal/create-info-modal.component.html +++ b/src/app/pages/create-game/create-info-modal/create-info-modal.component.html @@ -38,17 +38,7 @@
Audiobotschaft
Fotos und Grafiken
-
- - - - - - - - - -
+
Weitere Einstellungen
diff --git a/src/app/pages/create-game/create-info-modal/create-info-modal.component.ts b/src/app/pages/create-game/create-info-modal/create-info-modal.component.ts index 10b19160..0be1c18b 100644 --- a/src/app/pages/create-game/create-info-modal/create-info-modal.component.ts +++ b/src/app/pages/create-game/create-info-modal/create-info-modal.component.ts @@ -1,10 +1,6 @@ import { Component, OnInit, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core'; import { ModalController } from "@ionic/angular"; -import { Plugins, CameraResultType, CameraSource } from '@capacitor/core'; import { MapFeaturesModalPage } from './../map-features-modal/map-features-modal.page'; -import { environment } from 'src/environments/environment'; -import { FileTransfer, FileTransferObject } from '@ionic-native/file-transfer/ngx'; -import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; import { QuestionType, AnswerType } from 'src/app/models/types'; import { standardMapFeatures } from 'src/app/models/standardMapFeatures'; import { cloneDeep } from 'lodash'; @@ -24,8 +20,6 @@ export class CreateInfoModalComponent implements OnInit, OnChanges { constructor( public modalController: ModalController, - private transfer: FileTransfer, - private sanitizer: DomSanitizer ) { } ngOnInit() { @@ -53,33 +47,6 @@ export class CreateInfoModalComponent implements OnInit, OnChanges { this.taskChange.emit(this.task) } - async capturePhoto(library: boolean = false) { - - const image = await Plugins.Camera.getPhoto({ - quality: 50, - allowEditing: false, - resultType: CameraResultType.Uri, - source: library ? CameraSource.Photos : CameraSource.Camera, - width: 500 - }); - - this.task.question.photo = this.sanitizer.bypassSecurityTrustResourceUrl(image.webPath); - - this.uploading = true; - - const fileTransfer: FileTransferObject = this.transfer.create(); - fileTransfer.upload(image.path, `${environment.apiURL}/upload`).then(res => { - console.log(JSON.parse(res.response)) - const filename = JSON.parse(res.response).filename - this.task.question.photo = `${environment.apiURL}/file/${filename}` - this.uploading = false; - }) - .catch(err => { - console.log(err) - this.uploading = false; - }) - } - async presentMapFeaturesModal() { const modal = await this.modalController.create({ component: MapFeaturesModalPage, diff --git a/src/app/pages/create-game/form-elements/photo-upload-multiple-choice/photo-upload-multiple-choice.component.ts b/src/app/pages/create-game/form-elements/photo-upload-multiple-choice/photo-upload-multiple-choice.component.ts index 5c096cb1..447bb06b 100644 --- a/src/app/pages/create-game/form-elements/photo-upload-multiple-choice/photo-upload-multiple-choice.component.ts +++ b/src/app/pages/create-game/form-elements/photo-upload-multiple-choice/photo-upload-multiple-choice.component.ts @@ -1,8 +1,7 @@ -import { Component, OnInit, Input, Output, EventEmitter } from "@angular/core"; +import { Component, OnInit, Input, Output, EventEmitter, ChangeDetectorRef } from "@angular/core"; import { PopoverController } from "@ionic/angular"; import { PopoverComponent } from "src/app/popover/popover.component"; import { environment } from 'src/environments/environment'; -import { FileTransfer, FileTransferObject } from '@ionic-native/file-transfer/ngx'; import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; import { Plugins, CameraResultType, CameraSource } from '@capacitor/core'; @@ -24,8 +23,8 @@ export class PhotoUploadMultipleChoiceComponent implements OnInit { constructor( public popoverController: PopoverController, - private transfer: FileTransfer, - private sanitizer: DomSanitizer + private sanitizer: DomSanitizer, + private changeRef: ChangeDetectorRef ) { } ngOnInit(): void { @@ -47,21 +46,27 @@ export class PhotoUploadMultipleChoiceComponent implements OnInit { this.uploading[photoNumber] = true; - const fileTransfer: FileTransferObject = this.transfer.create(); - fileTransfer.onProgress(e => { - this.uploadingProgress[photoNumber] = e.loaded / e.total * 100 - }) - fileTransfer.upload(image.path, `${environment.apiURL}/upload`).then(res => { - console.log(JSON.parse(res.response)) - const filename = JSON.parse(res.response).filename - this.photos[photoNumber] = `${environment.apiURL}/file/${filename}` - this.photosChange.emit(this.photos) - this.uploading[photoNumber] = false; - }) - .catch(err => { - console.log(err) - this.uploading[photoNumber] = false; - }) + let blob = await fetch(image.webPath).then(r => r.blob()); + let formData = new FormData(); + formData.append("file", blob); + + const options = { + method: 'POST', + body: formData + }; + + const postResponse = await fetch(`${environment.apiURL}/upload`, options) + + if (!postResponse.ok) { + throw Error("File upload failed") + } + this.uploading[photoNumber] = false; + + const postResponseText = await postResponse.json() + const filename = postResponseText.filename + this.photos[photoNumber] = `${environment.apiURL}/file/${filename}` + this.changeRef.detectChanges(); + this.photosChange.emit(this.photos) } async showPopover(ev: any, text: string) { diff --git a/src/app/pages/create-game/form-elements/photo-upload/photo-upload.component.html b/src/app/pages/create-game/form-elements/photo-upload/photo-upload.component.html index fc7007ad..d0f6be3d 100644 --- a/src/app/pages/create-game/form-elements/photo-upload/photo-upload.component.html +++ b/src/app/pages/create-game/form-elements/photo-upload/photo-upload.component.html @@ -17,6 +17,9 @@
{{ config.label }} { - this.uploadingProgress = e.loaded / e.total * 100 - }) - - fileTransfer.upload(image.path, `${environment.apiURL}/upload`).then(res => { - console.log(JSON.parse(res.response)) - const filename = JSON.parse(res.response).filename - this.photo = `${environment.apiURL}/file/${filename}` - this.changeRef.detectChanges(); - this.photoChange.emit(this.photo) - this.uploading = false; - }) - .catch(err => { - console.log(err) - this.uploading = false; - }) + let blob = await fetch(image.webPath).then(r => r.blob()); + let formData = new FormData(); + formData.append("file", blob); + + const options = { + method: 'POST', + body: formData + }; + + const postResponse = await fetch(`${environment.apiURL}/upload`, options) + + if (!postResponse.ok) { + throw Error("File upload failed") + } + this.uploading = false; + + const postResponseText = await postResponse.json() + const filename = postResponseText.filename + this.photo = `${environment.apiURL}/file/${filename}` + this.changeRef.detectChanges(); + this.photoChange.emit(this.photo) } async showPopover(ev: any, text: string) { diff --git a/src/app/pages/edit-game/edit-game-tasks/edit-game-list.module.ts b/src/app/pages/edit-game/edit-game-tasks/edit-game-list.module.ts index bbd6d847..646ec45f 100644 --- a/src/app/pages/edit-game/edit-game-tasks/edit-game-list.module.ts +++ b/src/app/pages/edit-game/edit-game-tasks/edit-game-list.module.ts @@ -7,9 +7,6 @@ import { IonicModule } from '@ionic/angular'; import { EditGameListPage } from './edit-game-list.page'; -import { FileTransfer } from '@ionic-native/file-transfer/ngx'; -// import { WebView } from '@ionic-native/ionic-webview/ngx'; - const routes: Routes = [ { path: '', @@ -25,9 +22,6 @@ const routes: Routes = [ RouterModule.forChild(routes) ], declarations: [EditGameListPage], - providers: [ - FileTransfer, - // WebView - ] + providers: [] }) export class EditGameListPageModule { } diff --git a/src/app/pages/play-game/playing-game/playing-game.module.ts b/src/app/pages/play-game/playing-game/playing-game.module.ts index 64970a09..c507159a 100644 --- a/src/app/pages/play-game/playing-game/playing-game.module.ts +++ b/src/app/pages/play-game/playing-game/playing-game.module.ts @@ -7,9 +7,6 @@ import { IonicModule } from "@ionic/angular"; import { PlayingGamePage } from "./playing-game.page"; import { NgShufflePipeModule } from "angular-pipes"; - -import { FileTransfer } from '@ionic-native/file-transfer/ngx'; -// import { WebView } from '@ionic-native/ionic-webview/ngx'; import { KeywordPipe } from 'src/app/pipes/keyword.pipe'; import { LottieModule } from 'ngx-lottie'; @@ -45,9 +42,6 @@ const routes: Routes = [ MarkdownModule.forRoot(), ], declarations: [PlayingGamePage, KeywordPipe, FeedbackComponent], - providers: [ - FileTransfer, - // WebView - ] + providers: [] }) export class PlayingGamePageModule { } diff --git a/src/app/pages/play-game/playing-game/playing-game.page.html b/src/app/pages/play-game/playing-game/playing-game.page.html index 83891ee0..1101d73c 100644 --- a/src/app/pages/play-game/playing-game/playing-game.page.html +++ b/src/app/pages/play-game/playing-game/playing-game.page.html @@ -132,8 +132,8 @@
+ {{ task.settings["question-type"].settings["object-description"] }} +

-->
@@ -192,7 +192,7 @@

Wer spielt?

- Spieler {{i + 1}} + {{i + 1}} diff --git a/src/app/pages/play-game/playing-game/playing-game.page.scss b/src/app/pages/play-game/playing-game/playing-game.page.scss index abf28bcc..b9e0288b 100644 --- a/src/app/pages/play-game/playing-game/playing-game.page.scss +++ b/src/app/pages/play-game/playing-game/playing-game.page.scss @@ -42,6 +42,10 @@ ion-thumbnail { left: 10px; right: 10px; + @media screen and (max-width: 568px) { + margin: 0; + } + &.minimized { width: 50px; height: 50px; @@ -127,6 +131,15 @@ ion-thumbnail { font-size: 20px; text-align: center; + @media screen and (max-width: 767px) { + p { + margin: 0; + } + max-height: 33vh; + overflow: scroll; + font-size: inherit; + } + ::ng-deep ion-icon { margin: 0; } @@ -297,6 +310,10 @@ ion-thumbnail { height: 100%; width: fit-content; display: initial; + + @media screen and (max-width: 568px) { + max-height: 100px; + } } } @@ -311,8 +328,22 @@ ion-thumbnail { > img { max-height: 200px; + + @media screen and (max-width: 568px) { + max-height: 150px; + } + border-radius: 1rem; width: auto; } } } + +ion-label.player-counter { + border: 2px solid var(--ion-color-primary-shade); + pointer-events: none; + border-radius: 50%; + width: 24px; + height: 24px; + text-align: center; +} diff --git a/src/app/pages/play-game/playing-game/playing-game.page.ts b/src/app/pages/play-game/playing-game/playing-game.page.ts index 3016128b..9d14a2dd 100644 --- a/src/app/pages/play-game/playing-game/playing-game.page.ts +++ b/src/app/pages/play-game/playing-game/playing-game.page.ts @@ -5,11 +5,6 @@ import { OsmService } from "../../../services/osm.service"; import { TrackerService } from "../../../services/tracker.service"; import mapboxgl from "mapbox-gl"; import { Plugins, GeolocationPosition, Capacitor, CameraResultType, CameraSource } from '@capacitor/core'; - -import { - DeviceOrientation, - DeviceOrientationCompassHeading -} from "@ionic-native/device-orientation/ngx"; import { ModalController, NavController, @@ -17,8 +12,7 @@ import { } from "@ionic/angular"; import { environment } from "src/environments/environment"; import { Game } from "src/app/models/game"; -import { Subscription, Observable, Subscriber } from "rxjs"; -import booleanPointInPolygon from "@turf/boolean-point-in-polygon"; +import { Subscription } from "rxjs"; import { RotationControl, RotationType } from './../../../mapControllers/rotation-control' import { ViewDirectionControl, ViewDirectionType } from './../../../mapControllers/view-direction-control'; import { LandmarkControl } from 'src/app/mapControllers/landmark-control'; @@ -31,35 +25,19 @@ import { TrackControl, TrackType } from 'src/app/mapControllers/track-control'; import { GeolocateControl, GeolocateType } from 'src/app/mapControllers/geolocate-control'; import { MaskControl, MaskType } from 'src/app/mapControllers/mask-control'; import { PanControl, PanType } from 'src/app/mapControllers/pan-control'; - -import { FileTransfer, FileUploadOptions, FileTransferObject } from '@ionic-native/file-transfer/ngx'; -// import { WebView } from '@ionic-native/ionic-webview/ngx'; import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; - import { mappings } from './../../../pipes/keywords.js' - import { OrigamiGeolocationService } from './../../../services/origami-geolocation.service'; import { AnswerType, TaskMode, QuestionType } from 'src/app/models/types'; - import { cloneDeep } from 'lodash'; import { standardMapFeatures } from "../../../models/standardMapFeatures" - import { AnimationOptions } from 'ngx-lottie'; import bbox from '@turf/bbox'; import buffer from '@turf/buffer'; import { Task } from 'src/app/models/task'; -import { FeedbackComponent } from 'src/app/components/feedback/feedback.component'; import { point } from '@turf/helpers'; import booleanWithin from '@turf/boolean-within' - - -enum FeedbackType { - Correct, - Wrong, - TryAgain, - Saved, - Success -} +import { OrigamiOrientationService } from 'src/app/services/origami-orientation.service'; @Component({ selector: "app-playing-game", @@ -80,7 +58,6 @@ export class PlayingGamePage implements OnInit, OnDestroy { map: mapboxgl.Map; waypointMarker: mapboxgl.Marker; waypointMarkerDuplicate: mapboxgl.Marker; - // zoomControl: mapboxgl.NavigationControl = new mapboxgl.NavigationControl(); // map features directionArrow: boolean = false; @@ -120,13 +97,6 @@ export class PlayingGamePage implements OnInit, OnDestroy { public static showSuccess: boolean = false; public lottieConfig: AnimationOptions; - // showFeedback: boolean = false; - // feedback: any = { - // text: '', - // icon: '' - // } - // feedbackRetry: boolean = false; - Math: Math = Math; uploadDone: boolean = false; @@ -169,16 +139,15 @@ export class PlayingGamePage implements OnInit, OnDestroy { public toastController: ToastController, private gamesService: GamesService, public navCtrl: NavController, - private deviceOrientation: DeviceOrientation, private changeDetectorRef: ChangeDetectorRef, private OSMService: OsmService, private trackerService: TrackerService, public alertController: AlertController, public platform: Platform, public helperService: HelperService, - private transfer: FileTransfer, private sanitizer: DomSanitizer, private geolocationService: OrigamiGeolocationService, + private orientationService: OrigamiOrientationService ) { this.lottieConfig = { path: "assets/lottie/star-success.json", @@ -260,6 +229,7 @@ export class PlayingGamePage implements OnInit, OnDestroy { }); this.geolocationService.init(); + this.orientationService.init(); this.positionSubscription = this.geolocationService.geolocationSubscription.subscribe(position => { this.trackerService.addWaypoint({}); @@ -283,17 +253,17 @@ export class PlayingGamePage implements OnInit, OnDestroy { }); this.map.on("load", () => { - this.rotationControl = new RotationControl(this.map) + this.rotationControl = new RotationControl(this.map, this.orientationService) this.landmarkControl = new LandmarkControl(this.map) this.streetSectionControl = new StreetSectionControl(this.map, this.OSMService, this.geolocationService); - this.layerControl = new LayerControl(this.map, this.mapWrapper, this.deviceOrientation, this.alertController, this.platform) + this.layerControl = new LayerControl(this.map, this.mapWrapper, this.alertController, this.platform) this.trackControl = new TrackControl(this.map, this.geolocationService) this.geolocateControl = new GeolocateControl(this.map, this.geolocationService) - this.viewDirectionControl = new ViewDirectionControl(this.map, this.deviceOrientation, this.geolocationService) + this.viewDirectionControl = new ViewDirectionControl(this.map, this.geolocationService, this.orientationService) this.panControl = new PanControl(this.map, this.geolocationService) this.maskControl = new MaskControl(this.map, this.geolocationService) - this.feedbackControl.init(this.map, this.geolocationService, this.deviceOrientation, this.helperService, this.toastController, this.trackerService, this) + this.feedbackControl.init(this.map, this.geolocationService, this.helperService, this.toastController, this.trackerService, this) this.map.loadImage( "/assets/icons/directionv2-richtung.png", @@ -366,13 +336,11 @@ export class PlayingGamePage implements OnInit, OnDestroy { }); // rotation - this.deviceOrientationSubscription = this.deviceOrientation - .watchHeading() - .subscribe((data: DeviceOrientationCompassHeading) => { - this.compassHeading = data.magneticHeading; - this.targetHeading = 360 - (this.compassHeading - this.heading); - this.indicatedDirection = this.compassHeading - this.directionBearing; - }); + this.deviceOrientationSubscription = this.orientationService.orientationSubscription.subscribe((heading: number) => { + this.compassHeading = heading; + this.targetHeading = 360 - (this.compassHeading - this.heading); + this.indicatedDirection = this.compassHeading - this.directionBearing; + }) if (Capacitor.isNative) { Plugins.CapacitorKeepScreenOn.enable() @@ -1034,6 +1002,7 @@ export class PlayingGamePage implements OnInit, OnDestroy { navigateHome() { this.positionSubscription.unsubscribe(); this.geolocationService.clear() + this.deviceOrientationSubscription.unsubscribe(); this.trackerService.clear(); @@ -1050,6 +1019,8 @@ export class PlayingGamePage implements OnInit, OnDestroy { this.feedbackControl.remove(); + this.orientationService.clear() + // this.map.remove(); this.navCtrl.navigateRoot("/"); this.streetSectionControl.remove(); @@ -1075,21 +1046,26 @@ export class PlayingGamePage implements OnInit, OnDestroy { this.uploading = true; - const fileTransfer: FileTransferObject = this.transfer.create(); - fileTransfer.upload(image.path, `${environment.apiURL}/upload`).then(res => { - const filename = JSON.parse(res.response).filename - this.photoURL = `${environment.apiURL}/file/${filename}` - this.uploading = false; + let blob = await fetch(image.webPath).then(r => r.blob()); + let formData = new FormData(); + formData.append("file", blob); - this.trackerService.addEvent({ - type: "PHOTO_TAKEN", - photo: `${environment.apiURL}/file/${filename}` - }) - }) - .catch(err => { - console.log(err) - this.uploading = false; - }) + const options = { + method: 'POST', + body: formData + }; + + const postResponse = await fetch(`${environment.apiURL}/upload`, options) + + if (!postResponse.ok) { + throw Error("File upload failed") + } + this.uploading = false; + + const postResponseText = await postResponse.json() + const filename = postResponseText.filename + this.photoURL = `${environment.apiURL}/file/${filename}` + this.changeDetectorRef.detectChanges() } toggleRotate() { diff --git a/src/app/services/origami-orientation.service.ts b/src/app/services/origami-orientation.service.ts index d10b3458..8be7d20d 100644 --- a/src/app/services/origami-orientation.service.ts +++ b/src/app/services/origami-orientation.service.ts @@ -1,9 +1,58 @@ import { Injectable } from '@angular/core'; +import { AlertController, Platform } from '@ionic/angular'; +import { Observable, Subscriber, Subscription } from 'rxjs'; +import { shareReplay } from 'rxjs/operators'; +import { + DeviceOrientation, + DeviceOrientationCompassHeading +} from "@ionic-native/device-orientation/ngx"; @Injectable({ providedIn: 'root' }) export class OrigamiOrientationService { - constructor() { } + public orientationSubscription: Observable; + private orientationSubscriber: Subscriber + + deviceOrientationSubscription: any; + + constructor(private platform: Platform, private deviceOrientation: DeviceOrientation) { } + + init() { + console.log(this.platform.platforms()) + if (this.platform.is('mobile') && this.platform.is('capacitor') && !this.platform.is('mobileweb')) { + // native + this.deviceOrientationSubscription = this.deviceOrientation + .watchHeading({ + frequency: 10 + }) + .subscribe((data: DeviceOrientationCompassHeading) => { + this.orientationSubscriber.next(data.magneticHeading) + }) + } else { + // not native + if (this.platform.is('android')) { + // Android Chrome + window.addEventListener("deviceorientationabsolute", this.handleDeviceOrientation, true); + } else { + // everything else + window.addEventListener('deviceorientation', this.handleDeviceOrientation, true); + } + } + + this.orientationSubscription = new Observable((subscriber: Subscriber) => { + this.orientationSubscriber = subscriber + }).pipe(shareReplay()); + } + + handleDeviceOrientation = (event: DeviceOrientationEvent) => { + this.orientationSubscriber.next(360 - event.alpha) + } + + clear() { + window.removeEventListener("deviceorientation", this.handleDeviceOrientation, true) + window.removeEventListener("deviceorientationabsolute", this.handleDeviceOrientation, true) + this.orientationSubscriber.unsubscribe() + } } diff --git a/src/app/services/tracker.service.ts b/src/app/services/tracker.service.ts index 2a481361..e67037bb 100644 --- a/src/app/services/tracker.service.ts +++ b/src/app/services/tracker.service.ts @@ -5,9 +5,9 @@ import { Plugins, DeviceInfo, GeolocationPosition } from "@capacitor/core"; import { environment } from "../../environments/environment"; import { OrigamiGeolocationService } from './origami-geolocation.service'; import { Subscription } from 'rxjs'; -import { DeviceOrientation, DeviceOrientationCompassHeading } from '@ionic-native/device-orientation/ngx'; import { FilesystemDirectory, FilesystemEncoding } from '@capacitor/core'; +import { OrigamiOrientationService } from './origami-orientation.service'; @Injectable({ providedIn: "root" @@ -40,7 +40,7 @@ export class TrackerService { constructor( private http: HttpClient, private geolocateService: OrigamiGeolocationService, - private deviceOrientation: DeviceOrientation + private orientationService: OrigamiOrientationService ) { } async init(gameID, name, map: any, players: string[]) { @@ -48,22 +48,20 @@ export class TrackerService { this.position = position }) - this.deviceOrientationSubscription = this.deviceOrientation - .watchHeading() - .subscribe((data: DeviceOrientationCompassHeading) => { - if (this.lastHeading === undefined) { - this.lastHeading = data.magneticHeading - } + this.deviceOrientationSubscription = this.orientationService.orientationSubscription.subscribe((heading: number) => { + if (this.lastHeading === undefined) { + this.lastHeading = heading + } - let diff = Math.abs(this.lastHeading - data.magneticHeading) - diff = Math.abs((diff + 180) % 360 - 180) - if (diff > 15) { - this.rotationCounter += diff - this.lastHeading = data.magneticHeading - } + let diff = Math.abs(this.lastHeading - heading) + diff = Math.abs((diff + 180) % 360 - 180) + if (diff > 15) { + this.rotationCounter += diff + this.lastHeading = heading + } - this.compassHeading = data.magneticHeading; - }); + this.compassHeading = heading + }) this.map = map; this.map.on('moveend', moveEvent => { diff --git a/src/app/start/start.page.scss b/src/app/start/start.page.scss index c2d93e62..62360b43 100644 --- a/src/app/start/start.page.scss +++ b/src/app/start/start.page.scss @@ -7,6 +7,10 @@ .logo { margin-top: 30px; max-width: 280px; + + @media only screen and (max-width: 575px) { + max-width: 180px; + } } ion-card { diff --git a/src/assets/icon/favicon.png b/src/assets/icon/favicon.png index a1d5bbfb..dd71aba5 100644 Binary files a/src/assets/icon/favicon.png and b/src/assets/icon/favicon.png differ diff --git a/src/assets/icons/icon-128x128.png b/src/assets/icons/icon-128x128.png index d325ee46..457e7618 100644 Binary files a/src/assets/icons/icon-128x128.png and b/src/assets/icons/icon-128x128.png differ diff --git a/src/assets/icons/icon-144x144.png b/src/assets/icons/icon-144x144.png index 2b68f86f..d3e26efa 100644 Binary files a/src/assets/icons/icon-144x144.png and b/src/assets/icons/icon-144x144.png differ diff --git a/src/assets/icons/icon-152x152.png b/src/assets/icons/icon-152x152.png index b22fbac2..4fedc351 100644 Binary files a/src/assets/icons/icon-152x152.png and b/src/assets/icons/icon-152x152.png differ diff --git a/src/assets/icons/icon-192x192.png b/src/assets/icons/icon-192x192.png index d1a6fc4f..9159b43a 100644 Binary files a/src/assets/icons/icon-192x192.png and b/src/assets/icons/icon-192x192.png differ diff --git a/src/assets/icons/icon-384x384.png b/src/assets/icons/icon-384x384.png index 662ab81b..770a25c3 100644 Binary files a/src/assets/icons/icon-384x384.png and b/src/assets/icons/icon-384x384.png differ diff --git a/src/assets/icons/icon-512x512.png b/src/assets/icons/icon-512x512.png index 1c4a2e73..df20f17b 100644 Binary files a/src/assets/icons/icon-512x512.png and b/src/assets/icons/icon-512x512.png differ diff --git a/src/assets/icons/icon-72x72.png b/src/assets/icons/icon-72x72.png index 85ce9105..dd943aaa 100644 Binary files a/src/assets/icons/icon-72x72.png and b/src/assets/icons/icon-72x72.png differ diff --git a/src/assets/icons/icon-96x96.png b/src/assets/icons/icon-96x96.png index 9b7ee0e2..caf9ffe7 100644 Binary files a/src/assets/icons/icon-96x96.png and b/src/assets/icons/icon-96x96.png differ diff --git a/src/assets/icons/icon.png b/src/assets/icons/icon.png index bcceaedd..38667fbb 100644 Binary files a/src/assets/icons/icon.png and b/src/assets/icons/icon.png differ diff --git a/yarn.lock b/yarn.lock index f7d5b17e..ccbead44 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1143,13 +1143,6 @@ dependencies: "@types/cordova" latest -"@ionic-native/file-transfer@^5.24.0": - version "5.24.0" - resolved "https://registry.yarnpkg.com/@ionic-native/file-transfer/-/file-transfer-5.24.0.tgz#4d9c40bfd9421280a6e9ccc794720e8a4abc9320" - integrity sha512-FXZ2DWTARmcwqozARksuJANp7Kj3gkJhLVV6ROFQez04zqoU+0KDSDfk/K8aJw1cRUy7VT0H0B7kRAGmBozYXQ== - dependencies: - "@types/cordova" latest - "@ionic/angular-toolkit@^2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@ionic/angular-toolkit/-/angular-toolkit-2.2.0.tgz#0960898113a6022d0736aaa734c05af2c02ae508" @@ -3535,16 +3528,6 @@ cordova-plugin-device-orientation@^2.0.1: resolved "https://registry.yarnpkg.com/cordova-plugin-device-orientation/-/cordova-plugin-device-orientation-2.0.1.tgz#149d51250defc309f0d03d306fe28e89881ba84d" integrity sha1-FJ1RJQ3vwwnw0D0wb+KOiYgbqE0= -cordova-plugin-file-transfer@^1.7.1: - version "1.7.1" - resolved "https://registry.yarnpkg.com/cordova-plugin-file-transfer/-/cordova-plugin-file-transfer-1.7.1.tgz#a75d8be2ebc3bb9b23c5b1bbd199214ec267586b" - integrity sha1-p12L4uvDu5sjxbG70ZkhTsJnWGs= - -cordova-plugin-file@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/cordova-plugin-file/-/cordova-plugin-file-6.0.2.tgz#f3911479f8357e9aacb5576674f8d95b31a1fb20" - integrity sha512-m7cughw327CjONN/qjzsTpSesLaeybksQh420/gRuSXJX5Zt9NfgsSbqqKDon6jnQ9Mm7h7imgyO2uJ34XMBtA== - cordova-serve@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cordova-serve/-/cordova-serve-3.0.0.tgz#974d695f75a9f2b3e487604be6f539a39dba8390"