From 7fd80506a220f06540be860a0f258b552231e175 Mon Sep 17 00:00:00 2001 From: gruble Date: Sun, 17 May 2026 20:57:46 +0200 Subject: [PATCH 1/8] =?UTF-8?q?Fors=C3=B8k=20p=C3=A5=20fiks=20av=20rettigh?= =?UTF-8?q?etsproblem=20i=20Android.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ikke testet på tlf ennå --- android/app/src/main/AndroidManifest.xml | 1 - .../edit-images/edit-images.component.ts | 141 +++++++----------- 2 files changed, 52 insertions(+), 90 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index d51ba650b..f4dbd10c2 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -45,7 +45,6 @@ - diff --git a/src/app/modules/registration/components/edit-images/edit-images.component.ts b/src/app/modules/registration/components/edit-images/edit-images.component.ts index 9d845faab..7b1041e9a 100644 --- a/src/app/modules/registration/components/edit-images/edit-images.component.ts +++ b/src/app/modules/registration/components/edit-images/edit-images.component.ts @@ -17,11 +17,8 @@ import { } from '@ionic/angular/standalone'; import { Camera, - CameraResultType, - CameraSource, - GalleryImageOptions, - GalleryPhotos, - ImageOptions, + ChooseFromGalleryOptions, + TakePhotoOptions, } from '@capacitor/camera'; import { settings } from '../../../../../settings'; import { @@ -243,11 +240,11 @@ export class EditImagesComponent implements OnInit { buttons: [ { text: translations['REGISTRATION.GENERAL_COMMENT.TAKE_NEW_PHOTO'], - handler: () => this.getImages(CameraSource.Camera), + handler: () => this.takePhoto(), }, { text: translations['REGISTRATION.GENERAL_COMMENT.CHOOSE_FROM_LIBRARY'], - handler: () => this.getImages(CameraSource.Photos), + handler: () => this.chooseFromGallery(), }, { text: translations['DIALOGS.CANCEL'], @@ -258,78 +255,37 @@ export class EditImagesComponent implements OnInit { actionSheet.present(); } - private getImageOptions(source: CameraSource): ImageOptions { + private getTakePhotoOptions(): TakePhotoOptions { return { quality: settings.images.quality, - resultType: CameraResultType.Uri, - source: source, - height: settings.images.size, - width: settings.images.size, + targetHeight: settings.images.size, + targetWidth: settings.images.size, correctOrientation: true, + saveToGallery: true, + }; + } - // Lagrer appen alltid til bibliotek? - // Etter test på iOS: Nei, appen spør faktisk om å få lov til å lagre bilder i biblioteket, - // men kun om du ikke tidligere har lagt til bilder fra bibliotek. - // Dette er en native dialog og ikke noe vi aktivt spør om. - // Man får som sagt kun opp dialogen om man ikke har henta bilder fra biblioteket tidligere. - // Dette er ganske komplisert. - // Svarer man nei på dialogen lagres ikke bilder man tar via appen på telefonen, - // heller ikke neste gang man tar et nytt bilde. - // I innstillingene på telefonen kan man senere endre dette via "Tilgang til bildebiblioteket", der kan man velge - // enten "Ingen" eller "Kun legge til bilder". - // MEN! Hvis man senere velger å legge til bilder fra biblioteket på telefonen kan man velge å gi appen enten - // begrenset eller full tilgang. Uansett hva man velger, vil appen likevel lagre - // kamerabilder i bilioteket selv om man opprinnelig svarte nei. Dette kan være forvirrende for brukerne. - // Bør vi heller ha et valg i innstillingene for dette, kan være irriterende at appen alltid lagrer bilder i - // biblioteket? - saveToGallery: source === CameraSource.Camera, + private getChooseFromGalleryOptions(): ChooseFromGalleryOptions { + return { + quality: settings.images.quality, + allowMultipleSelection: true, }; } - /** - * Hent bilder fra bilde-biblioteket på telefonen. - * - * Sjekker først om appen har tillatelse, og spør evt om tillatelse hvis dette mangler. - * - * På iOS kan tillatelse-dialogen og plukk-bilder dialogen være forvirrende. Man kan nemlig velge mellom - * begrenset eller full tilgang til bildebiblioteket. Velger man begrenset (limited) får man opp en dialog der man - * kan velge hvilke bilder appen skal ha tilgang til fra bildebiblioteket. Dette er altså ikke hvilket bilde man vil - * legge til i observasjonen, men hvilke bilder appen skal ha tilgang til på et mer overordnet nivå. - * Meningen med dette er at appen deretter skal kunne implementere en egen dialog der brukeren kan velge mellom det - * begrensa utvalget bilder. Dette har ikke vi implementert. Derfor kan dette virke litt forvirrende, fordi man får - * opp to dialoger der man må velge bilder rett etter hverandre. Men dette skal kun skje første gang man spør om lov - * til å hente bilder fra bildebiblioteket, så det bør ikke være noe stort problem. - */ - private async getAlbumImageUrls(options: GalleryImageOptions): Promise { - let imageUrls: string[] = []; - let galleryPhotos: GalleryPhotos; - let permissionState = await Camera.checkPermissions(); - this.logger.debug('getAlbumImageUrls Camera.checkPermissions', DEBUG_TAG, { permissionState }); - if (!['granted', 'limited'].includes(permissionState?.photos)) { - permissionState = await Camera.requestPermissions({ permissions: ['photos'] }); - this.logger.debug('getAlbumImageUrls Camera.requestPermissions', DEBUG_TAG, { permissionState }); - } - if (['granted', 'limited'].includes(permissionState?.photos)) { - this.logger.debug('getAlbumImageUrls pickImages', DEBUG_TAG); - galleryPhotos = await Camera.pickImages(options); - this.logger.debug('getAlbumImageUrls pickImages result', DEBUG_TAG, { galleryPhotos }); - } else { - this.showErrorToast('REGISTRATION.IMAGE_ERROR.ALBUM_READ_PERMISSION_MISSING'); - this.logger.log('Could not get permissions to read from library', null, LogLevel.Warning, DEBUG_TAG); - return []; - } - if (galleryPhotos.photos.length > 0) { - if (this.checkAndNotifyIfUnsupportedImageFormat(galleryPhotos.photos.map((photo) => photo.format))) { - // TODO: photo.path kan være undefined, bør vi håndtere dette bedre? - imageUrls = galleryPhotos.photos.map((photo) => photo.path).filter((path) => path != null); - } - } + private async getAlbumImageUrls(options: ChooseFromGalleryOptions): Promise { + this.logger.debug('getAlbumImageUrls chooseFromGallery', DEBUG_TAG); + const result = await Camera.chooseFromGallery(options); + this.logger.debug('getAlbumImageUrls chooseFromGallery result', DEBUG_TAG, { result }); + + const imageUrls = result.results + .filter((media) => media.uri != null) + .map((media) => media.uri as string); this.logger.debug('getAlbumImageUrls result', DEBUG_TAG, { imageUrls }); return imageUrls; } - private async takePhotoAndReturnImageUrl(options: ImageOptions): Promise { + private async takePhotoAndReturnImageUrl(options: TakePhotoOptions): Promise { let permissionState = await Camera.checkPermissions(); this.logger.debug('takePhotoAndReturnImageUrl Camera.checkPermissions', DEBUG_TAG, { permissionState }); if (permissionState?.camera !== 'granted') { @@ -337,17 +293,14 @@ export class EditImagesComponent implements OnInit { this.logger.debug('takePhotoAndReturnImageUrl Camera.requestPermissions', DEBUG_TAG, { permissionState }); } if (permissionState?.camera === 'granted') { - const photo = await Camera.getPhoto(options); - this.logger.debug('takePhotoAndReturnImageUrl Camera.getPhoto', DEBUG_TAG, { + const photo = await Camera.takePhoto(options); + this.logger.debug('takePhotoAndReturnImageUrl Camera.takePhoto', DEBUG_TAG, { options, permissionState, - format: photo.format, saved: photo.saved, }); - if (photo) { - if (photo.path && this.checkAndNotifyIfUnsupportedImageFormat([photo.format])) { - return [photo.path]; - } + if (photo?.uri) { + return [photo.uri]; } } else { this.showErrorToast('REGISTRATION.IMAGE_ERROR.CAMERA_PERMISSION_MISSING'); @@ -355,41 +308,51 @@ export class EditImagesComponent implements OnInit { return []; } - private async getImages(source: CameraSource) { - this.logger.debug('getImages', DEBUG_TAG, { source }); + private async takePhoto() { + this.logger.debug('takePhoto', DEBUG_TAG); if (!this.platform.is('hybrid')) { //TODO: Gjøre som vi gjør på web for å hente bilde enten fra kamera eller album return true; } let imageUrls: string[] = []; try { - const options = this.getImageOptions(source); - if (source === CameraSource.Photos) { - imageUrls = await this.getAlbumImageUrls(options); - } else { - imageUrls = await this.takePhotoAndReturnImageUrl(options); - } + imageUrls = await this.takePhotoAndReturnImageUrl(this.getTakePhotoOptions()); for (const imageUrl of imageUrls) { this.logger.debug(`Got image url from camera plugin: ${imageUrl}`, DEBUG_TAG); await this.attachImageFileToDraft(imageUrl, MIME_TYPE); } } catch (err) { const hasMessage = err instanceof Error && err.message != null; - // we ignore errors we get if user cancels taking photo or gallery selection if (!hasMessage || !ERRORS_TO_IGNORE.includes(err.message)) { - this.logger.log('Unknown error when adding image', err, LogLevel.Warning, DEBUG_TAG, imageUrls); + this.logger.log('Unknown error when taking photo', err, LogLevel.Warning, DEBUG_TAG, imageUrls); this.showErrorToast('REGISTRATION.IMAGE_ERROR.UNKNOWN'); } } - this.logger.debug('getImages return', DEBUG_TAG, { source, nImages: imageUrls.length }); + this.logger.debug('takePhoto return', DEBUG_TAG, { nImages: imageUrls.length }); return true; } - private checkAndNotifyIfUnsupportedImageFormat(formats: string[]) { - if (formats.some((f) => f !== 'jpeg')) { - this.showErrorToast('REGISTRATION.INVALID_IMAGE'); - return false; + private async chooseFromGallery() { + this.logger.debug('chooseFromGallery', DEBUG_TAG); + if (!this.platform.is('hybrid')) { + //TODO: Gjøre som vi gjør på web for å hente bilde enten fra kamera eller album + return true; + } + let imageUrls: string[] = []; + try { + imageUrls = await this.getAlbumImageUrls(this.getChooseFromGalleryOptions()); + for (const imageUrl of imageUrls) { + this.logger.debug(`Got image url from camera plugin: ${imageUrl}`, DEBUG_TAG); + await this.attachImageFileToDraft(imageUrl, MIME_TYPE); + } + } catch (err) { + const hasMessage = err instanceof Error && err.message != null; + if (!hasMessage || !ERRORS_TO_IGNORE.includes(err.message)) { + this.logger.log('Unknown error when choosing from gallery', err, LogLevel.Warning, DEBUG_TAG, imageUrls); + this.showErrorToast('REGISTRATION.IMAGE_ERROR.UNKNOWN'); + } } + this.logger.debug('chooseFromGallery return', DEBUG_TAG, { nImages: imageUrls.length }); return true; } From ea6316e5ef8516444b810ec65ce1ca6c7c4cd6a0 Mon Sep 17 00:00:00 2001 From: gruble Date: Mon, 18 May 2026 10:00:05 +0200 Subject: [PATCH 2/8] =?UTF-8?q?Tatt=20inn=20igjen=20kommentarer=20som=20va?= =?UTF-8?q?r=20slettet=20og=20satt=20=C3=B8nsket=20format=20til=20jpg?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../edit-images/edit-images.component.ts | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/app/modules/registration/components/edit-images/edit-images.component.ts b/src/app/modules/registration/components/edit-images/edit-images.component.ts index 7b1041e9a..8ad6dc86d 100644 --- a/src/app/modules/registration/components/edit-images/edit-images.component.ts +++ b/src/app/modules/registration/components/edit-images/edit-images.component.ts @@ -15,11 +15,7 @@ import { Platform, ToastController, } from '@ionic/angular/standalone'; -import { - Camera, - ChooseFromGalleryOptions, - TakePhotoOptions, -} from '@capacitor/camera'; +import { Camera, ChooseFromGalleryOptions, EncodingType, TakePhotoOptions } from '@capacitor/camera'; import { settings } from '../../../../../settings'; import { AttachmentType, @@ -48,6 +44,7 @@ import { toSignal } from '@angular/core/rxjs-interop'; import { UserSettingService } from 'src/app/core/services/user-setting/user-setting.service'; import { RegobsAuthService } from 'src/app/modules/auth/services/regobs-auth.service'; import { getRoundedDownOrientationValue } from 'src/app/utils/getRoundedDownOrientationValue'; +import { Encoding } from '@capacitor/filesystem'; const DEBUG_TAG = 'AddPictureItemComponent'; const MIME_TYPE = 'image/jpeg'; @@ -255,12 +252,29 @@ export class EditImagesComponent implements OnInit { actionSheet.present(); } - private getTakePhotoOptions(): TakePhotoOptions { + private getImageOptions(): TakePhotoOptions { return { quality: settings.images.quality, targetHeight: settings.images.size, targetWidth: settings.images.size, correctOrientation: true, + encodingType: EncodingType.JPEG, + + // Lagrer appen alltid til bibliotek? + // Etter test på iOS: Nei, appen spør faktisk om å få lov til å lagre bilder i biblioteket, + // men kun om du ikke tidligere har lagt til bilder fra bibliotek. + // Dette er en native dialog og ikke noe vi aktivt spør om. + // Man får som sagt kun opp dialogen om man ikke har henta bilder fra biblioteket tidligere. + // Dette er ganske komplisert. + // Svarer man nei på dialogen lagres ikke bilder man tar via appen på telefonen, + // heller ikke neste gang man tar et nytt bilde. + // I innstillingene på telefonen kan man senere endre dette via "Tilgang til bildebiblioteket", der kan man velge + // enten "Ingen" eller "Kun legge til bilder". + // MEN! Hvis man senere velger å legge til bilder fra biblioteket på telefonen kan man velge å gi appen enten + // begrenset eller full tilgang. Uansett hva man velger, vil appen likevel lagre + // kamerabilder i bilioteket selv om man opprinnelig svarte nei. Dette kan være forvirrende for brukerne. + // Bør vi heller ha et valg i innstillingene for dette, kan være irriterende at appen alltid lagrer bilder i + // biblioteket? saveToGallery: true, }; } @@ -277,9 +291,7 @@ export class EditImagesComponent implements OnInit { const result = await Camera.chooseFromGallery(options); this.logger.debug('getAlbumImageUrls chooseFromGallery result', DEBUG_TAG, { result }); - const imageUrls = result.results - .filter((media) => media.uri != null) - .map((media) => media.uri as string); + const imageUrls = result.results.filter((media) => media.uri != null).map((media) => media.uri as string); this.logger.debug('getAlbumImageUrls result', DEBUG_TAG, { imageUrls }); return imageUrls; @@ -316,7 +328,7 @@ export class EditImagesComponent implements OnInit { } let imageUrls: string[] = []; try { - imageUrls = await this.takePhotoAndReturnImageUrl(this.getTakePhotoOptions()); + imageUrls = await this.takePhotoAndReturnImageUrl(this.getImageOptions()); for (const imageUrl of imageUrls) { this.logger.debug(`Got image url from camera plugin: ${imageUrl}`, DEBUG_TAG); await this.attachImageFileToDraft(imageUrl, MIME_TYPE); From 2e272ebd5f2dfe75e2ea3d486547ef29a515de83 Mon Sep 17 00:00:00 2001 From: gruble Date: Mon, 18 May 2026 10:00:32 +0200 Subject: [PATCH 3/8] Oppgradert til siste versjon av cakera-plugin --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8028058c9..53d433e4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,7 @@ "@capacitor/android": "^8.3.1", "@capacitor/app": "^8.1.0", "@capacitor/browser": "^8.0.3", - "@capacitor/camera": "^8.1.0", + "@capacitor/camera": "^8.2.0", "@capacitor/clipboard": "^8.0.1", "@capacitor/core": "^8.3.1", "@capacitor/device": "^8.0.2", @@ -2437,9 +2437,9 @@ } }, "node_modules/@capacitor/camera": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@capacitor/camera/-/camera-8.1.0.tgz", - "integrity": "sha512-ep12soG6c6HbfsEelLIM5Ip/YlWvd6H8pZsXg1gzjLNbTN6P/URUxO0Lv08bHyaDQVJkjLKthF7gaOG++TP4NQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@capacitor/camera/-/camera-8.2.0.tgz", + "integrity": "sha512-hYfrT6xpL936qoEkIpJzSnb0fQCaTkOux1cXzGBfH8QLOGqr6gSLiWZlZz/fqMPmMKJMNRBqlTQkj5fuMhVZog==", "license": "MIT", "peerDependencies": { "@capacitor/core": ">=8.0.0" diff --git a/package.json b/package.json index 98bc29498..78a062b24 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "@capacitor/android": "^8.3.1", "@capacitor/app": "^8.1.0", "@capacitor/browser": "^8.0.3", - "@capacitor/camera": "^8.1.0", + "@capacitor/camera": "^8.2.0", "@capacitor/clipboard": "^8.0.1", "@capacitor/core": "^8.3.1", "@capacitor/device": "^8.0.2", From 67dae6635ea8fc3c37e5b067162aaa11b04c3825 Mon Sep 17 00:00:00 2001 From: gruble Date: Mon, 18 May 2026 11:31:51 +0200 Subject: [PATCH 4/8] =?UTF-8?q?Sett=20=C3=B8nsket=20h=C3=B8yde=20og=20bred?= =?UTF-8?q?de=20p=C3=A5=20bilder=20fra=20album?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/edit-images/edit-images.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/modules/registration/components/edit-images/edit-images.component.ts b/src/app/modules/registration/components/edit-images/edit-images.component.ts index 8ad6dc86d..caec8c96d 100644 --- a/src/app/modules/registration/components/edit-images/edit-images.component.ts +++ b/src/app/modules/registration/components/edit-images/edit-images.component.ts @@ -44,7 +44,6 @@ import { toSignal } from '@angular/core/rxjs-interop'; import { UserSettingService } from 'src/app/core/services/user-setting/user-setting.service'; import { RegobsAuthService } from 'src/app/modules/auth/services/regobs-auth.service'; import { getRoundedDownOrientationValue } from 'src/app/utils/getRoundedDownOrientationValue'; -import { Encoding } from '@capacitor/filesystem'; const DEBUG_TAG = 'AddPictureItemComponent'; const MIME_TYPE = 'image/jpeg'; @@ -282,6 +281,8 @@ export class EditImagesComponent implements OnInit { private getChooseFromGalleryOptions(): ChooseFromGalleryOptions { return { quality: settings.images.quality, + targetWidth: settings.images.size, + targetHeight: settings.images.size, allowMultipleSelection: true, }; } From 40534627d1885339ae822e41a1385da57ac9e584 Mon Sep 17 00:00:00 2001 From: gruble Date: Mon, 18 May 2026 12:55:01 +0200 Subject: [PATCH 5/8] =?UTF-8?q?Vise=20feilmelding=20om=20vi=20pr=C3=B8ver?= =?UTF-8?q?=20=C3=A5=20hente=20noe=20annet=20enn=20jpeg=20fra=20album?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/edit-images/edit-images.component.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/app/modules/registration/components/edit-images/edit-images.component.ts b/src/app/modules/registration/components/edit-images/edit-images.component.ts index caec8c96d..86ecabb66 100644 --- a/src/app/modules/registration/components/edit-images/edit-images.component.ts +++ b/src/app/modules/registration/components/edit-images/edit-images.component.ts @@ -354,6 +354,9 @@ export class EditImagesComponent implements OnInit { let imageUrls: string[] = []; try { imageUrls = await this.getAlbumImageUrls(this.getChooseFromGalleryOptions()); + if (!this.checkAndNotifyIfUnsupportedImageFormat(imageUrls)) { + return false; + } for (const imageUrl of imageUrls) { this.logger.debug(`Got image url from camera plugin: ${imageUrl}`, DEBUG_TAG); await this.attachImageFileToDraft(imageUrl, MIME_TYPE); @@ -369,6 +372,16 @@ export class EditImagesComponent implements OnInit { return true; } + private checkAndNotifyIfUnsupportedImageFormat(imageUrls: string[]) { + for (const imageUrl of imageUrls) { + if (!imageUrl.toLowerCase().endsWith('.jpg') && !imageUrl.toLowerCase().endsWith('.jpeg')) { + this.showErrorToast('REGISTRATION.INVALID_IMAGE'); + return false; + } + } + return true; + } + private showErrorToast(messageKey: string) { this.translateService.get(messageKey).subscribe(async (translation) => { const toast = await this.toastController.create({ From 506ea852789bc79f1d32cbf2669cc6106c47ddf5 Mon Sep 17 00:00:00 2001 From: gruble Date: Mon, 18 May 2026 16:07:58 +0200 Subject: [PATCH 6/8] =?UTF-8?q?G=C3=A5tt=20tilbake=20til=20=C3=A5=20bruke?= =?UTF-8?q?=20Capacitor.pickImages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../edit-images/edit-images.component.ts | 86 +++++++++++++++---- 1 file changed, 69 insertions(+), 17 deletions(-) diff --git a/src/app/modules/registration/components/edit-images/edit-images.component.ts b/src/app/modules/registration/components/edit-images/edit-images.component.ts index 86ecabb66..062217f59 100644 --- a/src/app/modules/registration/components/edit-images/edit-images.component.ts +++ b/src/app/modules/registration/components/edit-images/edit-images.component.ts @@ -15,7 +15,7 @@ import { Platform, ToastController, } from '@ionic/angular/standalone'; -import { Camera, ChooseFromGalleryOptions, EncodingType, TakePhotoOptions } from '@capacitor/camera'; +import { Camera, ChooseFromGalleryOptions, EncodingType, GalleryPhotos, TakePhotoOptions } from '@capacitor/camera'; import { settings } from '../../../../../settings'; import { AttachmentType, @@ -287,17 +287,63 @@ export class EditImagesComponent implements OnInit { }; } - private async getAlbumImageUrls(options: ChooseFromGalleryOptions): Promise { - this.logger.debug('getAlbumImageUrls chooseFromGallery', DEBUG_TAG); - const result = await Camera.chooseFromGallery(options); - this.logger.debug('getAlbumImageUrls chooseFromGallery result', DEBUG_TAG, { result }); - - const imageUrls = result.results.filter((media) => media.uri != null).map((media) => media.uri as string); + /** + * Hent bilder fra bilde-biblioteket på telefonen. + * + * Sjekker først om appen har tillatelse, og spør evt om tillatelse hvis dette mangler. + * + * På iOS kan tillatelse-dialogen og plukk-bilder dialogen være forvirrende. Man kan nemlig velge mellom + * begrenset eller full tilgang til bildebiblioteket. Velger man begrenset (limited) får man opp en dialog der man + * kan velge hvilke bilder appen skal ha tilgang til fra bildebiblioteket. Dette er altså ikke hvilket bilde man vil + * legge til i observasjonen, men hvilke bilder appen skal ha tilgang til på et mer overordnet nivå. + * Meningen med dette er at appen deretter skal kunne implementere en egen dialog der brukeren kan velge mellom det + * begrensa utvalget bilder. Dette har ikke vi implementert. Derfor kan dette virke litt forvirrende, fordi man får + * opp to dialoger der man må velge bilder rett etter hverandre. Men dette skal kun skje første gang man spør om lov + * til å hente bilder fra bildebiblioteket, så det bør ikke være noe stort problem. + */ + private async getAlbumImageUrls(): Promise { + let imageUrls: string[] = []; + let galleryPhotos: GalleryPhotos; + let permissionState = await Camera.checkPermissions(); + this.logger.debug('getAlbumImageUrls Camera.checkPermissions', DEBUG_TAG, { permissionState }); + if (!['granted', 'limited'].includes(permissionState?.photos)) { + permissionState = await Camera.requestPermissions({ permissions: ['photos'] }); + this.logger.debug('getAlbumImageUrls Camera.requestPermissions', DEBUG_TAG, { permissionState }); + } + if (['granted', 'limited'].includes(permissionState?.photos)) { + this.logger.debug('getAlbumImageUrls pickImages', DEBUG_TAG); + const options = this.getChooseFromGalleryOptions(); + galleryPhotos = await Camera.pickImages(options); + this.logger.debug('getAlbumImageUrls pickImages result', DEBUG_TAG, { galleryPhotos }); + } else { + this.showErrorToast('REGISTRATION.IMAGE_ERROR.ALBUM_READ_PERMISSION_MISSING'); + this.logger.log('Could not get permissions to read from library', null, LogLevel.Warning, DEBUG_TAG); + return []; + } + if (galleryPhotos.photos.length > 0) { + if (this.checkAndNotifyIfUnsupportedImageFormat(galleryPhotos.photos.map((photo) => photo.format))) { + // TODO: photo.path kan være undefined, bør vi håndtere dette bedre? + imageUrls = galleryPhotos.photos.map((photo) => photo.path).filter((path) => path != null); + } + } this.logger.debug('getAlbumImageUrls result', DEBUG_TAG, { imageUrls }); return imageUrls; } + // TODO: Bruker nyere API for å hente bilder fra album. Ikke tatt i bruk ennå fordi den ikke konverterer HEIC-bilder til JPEG + // private async getAlbumImageUrlsV2(): Promise { + // this.logger.debug('getAlbumImageUrls chooseFromGallery', DEBUG_TAG); + // const options = this.getChooseFromGalleryOptions(); + // const result = await Camera.chooseFromGallery(options); + // this.logger.debug('getAlbumImageUrls chooseFromGallery result', DEBUG_TAG, { result }); + + // const imageUrls = result.results.filter((media) => media.uri != null).map((media) => media.uri as string); + + // this.logger.debug('getAlbumImageUrls result', DEBUG_TAG, { imageUrls }); + // return imageUrls; + // } + private async takePhotoAndReturnImageUrl(options: TakePhotoOptions): Promise { let permissionState = await Camera.checkPermissions(); this.logger.debug('takePhotoAndReturnImageUrl Camera.checkPermissions', DEBUG_TAG, { permissionState }); @@ -353,10 +399,7 @@ export class EditImagesComponent implements OnInit { } let imageUrls: string[] = []; try { - imageUrls = await this.getAlbumImageUrls(this.getChooseFromGalleryOptions()); - if (!this.checkAndNotifyIfUnsupportedImageFormat(imageUrls)) { - return false; - } + imageUrls = await this.getAlbumImageUrls(); for (const imageUrl of imageUrls) { this.logger.debug(`Got image url from camera plugin: ${imageUrl}`, DEBUG_TAG); await this.attachImageFileToDraft(imageUrl, MIME_TYPE); @@ -372,16 +415,25 @@ export class EditImagesComponent implements OnInit { return true; } - private checkAndNotifyIfUnsupportedImageFormat(imageUrls: string[]) { - for (const imageUrl of imageUrls) { - if (!imageUrl.toLowerCase().endsWith('.jpg') && !imageUrl.toLowerCase().endsWith('.jpeg')) { - this.showErrorToast('REGISTRATION.INVALID_IMAGE'); - return false; - } + private checkAndNotifyIfUnsupportedImageFormat(formats: string[]) { + if (formats.some((f) => f !== 'jpeg')) { + this.showErrorToast('REGISTRATION.INVALID_IMAGE'); + return false; } return true; } + // TODO: Nyere og dummere måte å sjekke bildeformatet på, må kanskje bruke denne når vi bytter til å bruke Capacitor.pickImages. + // private checkAndNotifyIfUnsupportedImageFormatV2(imageUrls: string[]) { + // for (const imageUrl of imageUrls) { + // if (!imageUrl.toLowerCase().endsWith('jpg') && !imageUrl.toLowerCase().endsWith('jpeg')) { + // this.showErrorToast('REGISTRATION.INVALID_IMAGE'); + // return false; + // } + // } + // return true; + // } + private showErrorToast(messageKey: string) { this.translateService.get(messageKey).subscribe(async (translation) => { const toast = await this.toastController.create({ From 814e308795aa1b7699c8bc951eb551254deb40f7 Mon Sep 17 00:00:00 2001 From: gruble Date: Tue, 19 May 2026 09:42:35 +0200 Subject: [PATCH 7/8] =?UTF-8?q?Tatt=20med=20valg=20for=20oppl=C3=B8sning?= =?UTF-8?q?=20og=20orientering?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit for henting av bilder fra album --- .../components/edit-images/edit-images.component.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/modules/registration/components/edit-images/edit-images.component.ts b/src/app/modules/registration/components/edit-images/edit-images.component.ts index 062217f59..ebfceb449 100644 --- a/src/app/modules/registration/components/edit-images/edit-images.component.ts +++ b/src/app/modules/registration/components/edit-images/edit-images.component.ts @@ -15,7 +15,7 @@ import { Platform, ToastController, } from '@ionic/angular/standalone'; -import { Camera, ChooseFromGalleryOptions, EncodingType, GalleryPhotos, TakePhotoOptions } from '@capacitor/camera'; +import { Camera, EncodingType, GalleryImageOptions, GalleryPhotos, TakePhotoOptions } from '@capacitor/camera'; import { settings } from '../../../../../settings'; import { AttachmentType, @@ -278,12 +278,12 @@ export class EditImagesComponent implements OnInit { }; } - private getChooseFromGalleryOptions(): ChooseFromGalleryOptions { + private getChooseFromGalleryOptions(): GalleryImageOptions { return { quality: settings.images.quality, - targetWidth: settings.images.size, - targetHeight: settings.images.size, - allowMultipleSelection: true, + width: settings.images.size, + height: settings.images.size, + correctOrientation: true, }; } From 1604efe826d165d4b3ea8d463f3c729463c486e0 Mon Sep 17 00:00:00 2001 From: gruble Date: Tue, 19 May 2026 09:43:19 +0200 Subject: [PATCH 8/8] Fjernet utkommentert kode --- .../edit-images/edit-images.component.ts | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/src/app/modules/registration/components/edit-images/edit-images.component.ts b/src/app/modules/registration/components/edit-images/edit-images.component.ts index ebfceb449..9f676c078 100644 --- a/src/app/modules/registration/components/edit-images/edit-images.component.ts +++ b/src/app/modules/registration/components/edit-images/edit-images.component.ts @@ -331,19 +331,6 @@ export class EditImagesComponent implements OnInit { return imageUrls; } - // TODO: Bruker nyere API for å hente bilder fra album. Ikke tatt i bruk ennå fordi den ikke konverterer HEIC-bilder til JPEG - // private async getAlbumImageUrlsV2(): Promise { - // this.logger.debug('getAlbumImageUrls chooseFromGallery', DEBUG_TAG); - // const options = this.getChooseFromGalleryOptions(); - // const result = await Camera.chooseFromGallery(options); - // this.logger.debug('getAlbumImageUrls chooseFromGallery result', DEBUG_TAG, { result }); - - // const imageUrls = result.results.filter((media) => media.uri != null).map((media) => media.uri as string); - - // this.logger.debug('getAlbumImageUrls result', DEBUG_TAG, { imageUrls }); - // return imageUrls; - // } - private async takePhotoAndReturnImageUrl(options: TakePhotoOptions): Promise { let permissionState = await Camera.checkPermissions(); this.logger.debug('takePhotoAndReturnImageUrl Camera.checkPermissions', DEBUG_TAG, { permissionState }); @@ -423,17 +410,6 @@ export class EditImagesComponent implements OnInit { return true; } - // TODO: Nyere og dummere måte å sjekke bildeformatet på, må kanskje bruke denne når vi bytter til å bruke Capacitor.pickImages. - // private checkAndNotifyIfUnsupportedImageFormatV2(imageUrls: string[]) { - // for (const imageUrl of imageUrls) { - // if (!imageUrl.toLowerCase().endsWith('jpg') && !imageUrl.toLowerCase().endsWith('jpeg')) { - // this.showErrorToast('REGISTRATION.INVALID_IMAGE'); - // return false; - // } - // } - // return true; - // } - private showErrorToast(messageKey: string) { this.translateService.get(messageKey).subscribe(async (translation) => { const toast = await this.toastController.create({