diff --git a/apps/journal/package.json b/apps/journal/package.json
index e0115978..d7dae0b5 100644
--- a/apps/journal/package.json
+++ b/apps/journal/package.json
@@ -1,18 +1,19 @@
{
"name": "journal",
"devDependencies": {
- "@capacitor-community/fcm": "^5.0.2",
- "@capacitor-firebase/authentication": "^5.2.0",
- "@capacitor/app": "^5.0.6",
- "@capacitor/camera": "^5.0.7",
- "@capacitor/cli": "^5.0.5",
- "@capacitor/clipboard": "^5.0.6",
- "@capacitor/ios": "5.0.5",
- "@capacitor/keyboard": "^5.0.6",
- "@capacitor/push-notifications": "^5.1.0",
- "@capacitor/share": "^5.0.6",
- "@capacitor/splash-screen": "^5.0.6",
- "@capawesome/capacitor-app-update": "^5.0.1",
+ "@capacitor-community/fcm": "^5.0.3",
+ "@capacitor-firebase/authentication": "^6.0.0",
+ "@capacitor/app": "^6.0.0",
+ "@capacitor/camera": "^6.0.0",
+ "@capacitor/cli": "^6.0.0",
+ "@capacitor/clipboard": "^6.0.0",
+ "@capacitor/ios": "6.0.0",
+ "@capacitor/keyboard": "^6.0.0",
+ "@capacitor/push-notifications": "^6.0.0",
+ "@capacitor/share": "^6.0.0",
+ "@capacitor/splash-screen": "^6.0.0",
+ "@capawesome/capacitor-app-update": "^6.0.0",
+ "@capawesome/capacitor-file-picker": "^6.0.1",
"@capacitor/filesystem": "^6.0.0",
"send-intent": "^6.0.0"
}
diff --git a/libs/media/src/lib/components/images-selector/images-selector.component.html b/libs/media/src/lib/components/images-selector/images-selector.component.html
index f8a132fe..6c2c092d 100644
--- a/libs/media/src/lib/components/images-selector/images-selector.component.html
+++ b/libs/media/src/lib/components/images-selector/images-selector.component.html
@@ -1,7 +1,11 @@
@for (control of form.controls; track control; let index = $index) {
-
+ @if (control.value.type === 'video') {
+
+ } @else {
+
+ }
}
@@ -11,7 +15,7 @@
@case ('drop') {
- Add Photo
+ Add Photo or Video
}
diff --git a/libs/media/src/lib/components/images-selector/images-selector.component.scss b/libs/media/src/lib/components/images-selector/images-selector.component.scss
index 47fb48b5..03f67e3d 100644
--- a/libs/media/src/lib/components/images-selector/images-selector.component.scss
+++ b/libs/media/src/lib/components/images-selector/images-selector.component.scss
@@ -17,7 +17,7 @@
}
}
- img {
+ img, video {
height: 100%;
border-radius: 12px;
}
@@ -52,7 +52,7 @@
align-items: center;
margin-bottom: 1em;
- img {
+ img, video {
width: 100px;
}
}
diff --git a/libs/media/src/lib/components/images-selector/images-selector.component.ts b/libs/media/src/lib/components/images-selector/images-selector.component.ts
index a02ee02f..d111f532 100644
--- a/libs/media/src/lib/components/images-selector/images-selector.component.ts
+++ b/libs/media/src/lib/components/images-selector/images-selector.component.ts
@@ -2,18 +2,20 @@ import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, ChangeDetectorRef, Com
import { CommonModule } from '@angular/common'
import { FormArray } from '@angular/forms'
import { SafeUrl } from '@angular/platform-browser'
-import { IonIcon, PopoverController, ToastController } from '@ionic/angular/standalone'
+import { AlertController, IonIcon, PopoverController, ToastController } from '@ionic/angular/standalone'
import { addIcons } from 'ionicons'
import { imagesOutline } from 'ionicons/icons'
import { SwiperContainer } from 'swiper/swiper-element'
import { BehaviorSubject, Subscription } from 'rxjs'
-import { Camera, GalleryPhoto } from '@capacitor/camera'
+import { FilePicker } from '@capawesome/capacitor-file-picker'
+
import { captureException, captureMessage } from '@sentry/capacitor'
import { EditMediaForm } from '@strive/media/forms/media.form'
import { delay } from '@strive/utils/helpers'
import { ImageOptionsPopoverComponent } from './popover/options.component'
+import { VideoPlayerComponent } from '../video-player/video-player.component'
type CropStep = 'drop' | 'hovering'
@@ -27,7 +29,8 @@ type CropStep = 'drop' | 'hovering'
imports: [
CommonModule,
ImageOptionsPopoverComponent,
- IonIcon
+ IonIcon,
+ VideoPlayerComponent
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
@@ -35,7 +38,8 @@ export class ImagesSelectorComponent implements OnInit, OnDestroy {
step = new BehaviorSubject('drop')
- accept = ['.jpg', '.jpeg', '.png', '.webp']
+ accept = ['image/*','video/*']
+ maxVideoLength = 10
previewUrl$ = new BehaviorSubject('')
sub?: Subscription
@@ -46,6 +50,7 @@ export class ImagesSelectorComponent implements OnInit, OnDestroy {
@ViewChild('swiper') swiper?: ElementRef;
constructor(
+ private alertCtrl: AlertController,
private cdr: ChangeDetectorRef,
private popoverCtrl: PopoverController,
private toast: ToastController
@@ -86,15 +91,15 @@ export class ImagesSelectorComponent implements OnInit, OnDestroy {
async selectImages() {
try {
- const images = await Camera.pickImages({ quality: 100 })
- for (const image of images.photos) {
- const file = await getFileFromGalleryPhoto(image)
- if (file) {
- this.filesSelected(file)
+ const { files } = await FilePicker.pickMedia()
+
+ for (const file of files) {
+ if (file.blob instanceof File) {
+ this.filesSelected(file.blob)
} else {
this.toast.create({ message: 'Something went wrong', duration: 3000 })
captureMessage('Unsupported file type chosen')
- captureException(image)
+ captureException(file)
}
}
} catch (err) {
@@ -111,13 +116,14 @@ export class ImagesSelectorComponent implements OnInit, OnDestroy {
const files = isFileList(file) ? Array.from(file) : [file]
for (const file of files) {
- if (file.type?.split('/')[0] !== 'image') {
+ const type = file.type?.split('/')[0]
+ if (type !== 'image' && type !== 'video') {
this.toast.create({ message: 'Unsupported file type', duration: 3000 }).then(toast => toast.present())
return
}
const preview = URL.createObjectURL(file)
- const mediaForm = new EditMediaForm({ id: '', preview, file })
+ const mediaForm = new EditMediaForm({ id: '', preview, file, type })
this.form.push(mediaForm)
this.form.markAsDirty()
}
@@ -137,9 +143,11 @@ export class ImagesSelectorComponent implements OnInit, OnDestroy {
}
async openPopover(event: Event, index: number) {
+ const ctrl = this.form.at(index)
+ const type = ctrl.type.value
const popover = await this.popoverCtrl.create({
component: ImageOptionsPopoverComponent,
- componentProps: { form: this.form, index },
+ componentProps: { type },
event
})
@@ -153,23 +161,19 @@ export class ImagesSelectorComponent implements OnInit, OnDestroy {
popover.present()
}
+
+ checkDuration(event: any, index: number) {
+ const duration = event.target.duration
+ if (duration > this.maxVideoLength) {
+ this.form.removeAt(index)
+ this.alertCtrl.create({
+ subHeader: `Video cannot be longer than ${this.maxVideoLength} seconds`,
+ buttons: [{ text: 'Ok', role: 'cancel' }]
+ }).then(alert => alert.present())
+ }
+ }
}
function isFileList(file: FileList | File): file is FileList {
return (file as FileList).item !== undefined
-}
-
-
-async function getFileFromGalleryPhoto(photo: GalleryPhoto): Promise {
- try {
- const response = await fetch(photo.webPath);
- const blob = await response.blob();
- const name = photo.webPath.split('/').pop();
- const fileName = `${name}.${photo.format}`
- const file = new File([blob], fileName, { type: blob.type });
- return file;
- } catch (error) {
- console.error("Error fetching blob data:", error);
- return;
- }
-}
+}
\ No newline at end of file
diff --git a/libs/media/src/lib/components/images-selector/popover/options.component.ts b/libs/media/src/lib/components/images-selector/popover/options.component.ts
index 591a77a2..4da33fe0 100644
--- a/libs/media/src/lib/components/images-selector/popover/options.component.ts
+++ b/libs/media/src/lib/components/images-selector/popover/options.component.ts
@@ -1,5 +1,6 @@
-import { ChangeDetectionStrategy, Component } from '@angular/core'
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
import { IonList, IonItem, PopoverController } from '@ionic/angular/standalone'
+import { MediaType } from '@strive/model'
@Component({
standalone: true,
@@ -10,12 +11,15 @@ import { IonList, IonItem, PopoverController } from '@ionic/angular/standalone'
selector: 'strive-image-options-popover',
template: `
- Remove image
+ Remove {{ type }}
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ImageOptionsPopoverComponent {
+
+ @Input() type: MediaType = 'image'
+
constructor(
private popoverCtrl: PopoverController
) { }
diff --git a/libs/media/src/lib/components/video-player/video-player.component.html b/libs/media/src/lib/components/video-player/video-player.component.html
new file mode 100644
index 00000000..41c94871
--- /dev/null
+++ b/libs/media/src/lib/components/video-player/video-player.component.html
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/libs/media/src/lib/components/video-player/video-player.component.scss b/libs/media/src/lib/components/video-player/video-player.component.scss
new file mode 100644
index 00000000..64b6713f
--- /dev/null
+++ b/libs/media/src/lib/components/video-player/video-player.component.scss
@@ -0,0 +1,4 @@
+video {
+ width: 100%;
+ max-width: 1500px;
+}
\ No newline at end of file
diff --git a/libs/media/src/lib/components/video-player/video-player.component.ts b/libs/media/src/lib/components/video-player/video-player.component.ts
new file mode 100644
index 00000000..bfb8033b
--- /dev/null
+++ b/libs/media/src/lib/components/video-player/video-player.component.ts
@@ -0,0 +1,38 @@
+import { AfterViewInit, Component, ElementRef, Input, ViewChild, ViewEncapsulation } from '@angular/core'
+import { MediaRefPipe, VideoUrlPipe } from '@strive/media/pipes/media.pipe';
+// import Hls from 'hls.js'
+
+@Component({
+ standalone: true,
+ selector: 'media-video-player',
+ templateUrl: './video-player.component.html',
+ styleUrls: ['./video-player.component.scss'],
+ encapsulation: ViewEncapsulation.None,
+ imports: [
+ MediaRefPipe,
+ VideoUrlPipe
+ ]
+})
+export class VideoPlayerComponent implements AfterViewInit {
+ @ViewChild('player', { static: true }) player: ElementRef = {} as ElementRef;
+
+ @Input() storagePath = ''
+
+ ngAfterViewInit() {
+ // Vidoe API only available on IMGIX enterprice plan: https://www.imgix.com/pricing
+ // if (!this.storagePath) return
+
+ // const video = this.player.nativeElement
+ // const src = `https://${environment.firebase.options.projectId}.imgix.video/${encodeURI(this.storagePath)}?fm=hls`
+
+ // if (video.canPlayType('application/vnd.apple.mpegurl')) {
+ // video.src = src
+ // } else if (Hls.isSupported()) {
+ // const hls = new Hls()
+ // hls.loadSource(src)
+ // hls.attachMedia(video)
+ // } else {
+ // console.error("This is a legacy browser that doesn't support Media Source Extensions")
+ // }
+ }
+}
\ No newline at end of file
diff --git a/libs/media/src/lib/forms/media.form.ts b/libs/media/src/lib/forms/media.form.ts
index c9260417..1938907d 100644
--- a/libs/media/src/lib/forms/media.form.ts
+++ b/libs/media/src/lib/forms/media.form.ts
@@ -1,12 +1,13 @@
import { AbstractControl, FormControl, FormGroup } from '@angular/forms'
-import { Media } from '@strive/model'
+import { Media, MediaType } from '@strive/model'
import { getImgIxResourceUrl } from '../directives/imgix-helpers'
-interface EditMedia {
+export interface EditMedia {
id: string
preview: string
delete?: boolean
file?: File
+ type?: MediaType
}
export function mediaToEditMedia(media: Media): EditMedia {
@@ -21,6 +22,7 @@ export function mediaToEditMedia(media: Media): EditMedia {
return {
id: media.id ?? '',
preview: getPreview(),
+ type: media.fileType,
delete: false
}
}
@@ -30,6 +32,7 @@ export function createEditMedia(params: Partial = {}): EditMedia {
id: params.id ?? '',
preview: params.preview ?? '',
file: params.file ?? undefined,
+ type: params.type ?? undefined,
delete: params.delete ?? false
}
}
@@ -41,6 +44,7 @@ function createEditMediaFormControl(params: Partial = {}) {
delete: new FormControl(editMedia.delete ?? false, { nonNullable: true }),
preview: new FormControl(editMedia.preview, { nonNullable: true }),
file: new FormControl(editMedia.file, { nonNullable: true }),
+ type: new FormControl(editMedia.type, { nonNullable: true }),
id: new FormControl(editMedia.id ?? '', { nonNullable: true })
}
}
@@ -55,5 +59,6 @@ export class EditMediaForm extends FormGroup {
get delete() { return this.get('delete') as AbstractControl }
get preview() { return this.get('preview') as AbstractControl }
get file() { return this.get('file') as AbstractControl }
+ get type() { return this.get('type') as AbstractControl }
get id() { return this.get('id') as AbstractControl }
}
\ No newline at end of file
diff --git a/libs/media/src/lib/media.service.ts b/libs/media/src/lib/media.service.ts
index c6efb858..ea7547cf 100644
--- a/libs/media/src/lib/media.service.ts
+++ b/libs/media/src/lib/media.service.ts
@@ -31,9 +31,12 @@ export class MediaService extends FireSubCollection {
}
async upload(file: File, storagePath: string, goalId: string) {
+ const start = file.type.split('/')[0]
+ const fileType = start === 'image' ? 'image' : 'video'
+
const media = createMedia({
fileName: file.name,
- fileType: 'image',
+ fileType,
storagePath
})
const mediaId = await this.add(media, { params: { goalId }})
diff --git a/libs/media/src/lib/pipes/media.pipe.ts b/libs/media/src/lib/pipes/media.pipe.ts
index 4db234f6..c3415af3 100644
--- a/libs/media/src/lib/pipes/media.pipe.ts
+++ b/libs/media/src/lib/pipes/media.pipe.ts
@@ -1,6 +1,6 @@
import { Pipe, PipeTransform } from '@angular/core'
import { Media } from '@strive/model'
-
+import { environment } from '@env'
@Pipe({ name: 'mediaRef', standalone: true })
export class MediaRefPipe implements PipeTransform {
@@ -9,3 +9,11 @@ export class MediaRefPipe implements PipeTransform {
return `${media.storagePath}/${media.id}`
}
}
+
+@Pipe({ name: 'videoUrl', standalone: true })
+export class VideoUrlPipe implements PipeTransform {
+ transform(storagePath: string) {
+ if (!storagePath) return ''
+ return `https://${environment.firebase.options.projectId}.imgix.net/${encodeURI(storagePath)}?fm=hls`
+ }
+}
\ No newline at end of file
diff --git a/libs/model/src/lib/media.ts b/libs/model/src/lib/media.ts
index fcef63de..6be83fb7 100644
--- a/libs/model/src/lib/media.ts
+++ b/libs/model/src/lib/media.ts
@@ -3,7 +3,7 @@ const mediaTypes = [
'video',
'youtube'
] as const;
-type MediaType = typeof mediaTypes[number];
+export type MediaType = typeof mediaTypes[number];
type MediaUploadStatus = 'uploading' | 'uploaded' | 'error';
diff --git a/libs/post/src/lib/components/post/post.component.html b/libs/post/src/lib/components/post/post.component.html
index 3b6513b2..78b74017 100644
--- a/libs/post/src/lib/components/post/post.component.html
+++ b/libs/post/src/lib/components/post/post.component.html
@@ -34,8 +34,15 @@
}
@for (media of post.medias; track media; let index = $index) {
-
-
+
+ @if (media.fileType === 'video') {
+
+
+ } @else {
+
+ }
}
diff --git a/libs/post/src/lib/components/post/post.component.scss b/libs/post/src/lib/components/post/post.component.scss
index bcd959fa..9225aece 100644
--- a/libs/post/src/lib/components/post/post.component.scss
+++ b/libs/post/src/lib/components/post/post.component.scss
@@ -42,17 +42,36 @@ ion-card {
}
swiper-slide {
+ position: relative;
max-height: 600px;
height: auto;
max-width: 100%;
width: initial;
+ cursor: pointer;
- img {
+ img, video {
max-width: 100%;
width: 100%;
height: 100%;
object-fit: cover;
}
+
+ .video-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background: rgba(0,0,0,.1);
+
+ ion-icon {
+ font-size: 64px;
+ color: var(--ion-color-primary);
+ }
+ }
}
.strava-wrapper {
diff --git a/libs/post/src/lib/components/post/post.component.ts b/libs/post/src/lib/components/post/post.component.ts
index 03566a5e..552373cc 100644
--- a/libs/post/src/lib/components/post/post.component.ts
+++ b/libs/post/src/lib/components/post/post.component.ts
@@ -5,7 +5,7 @@ import { ModalController, PopoverController } from '@ionic/angular/standalone'
import { IonCard, IonAvatar, IonButton, IonIcon, IonCardContent } from '@ionic/angular/standalone'
import { addIcons } from 'ionicons'
-import { ellipsisVerticalOutline } from 'ionicons/icons'
+import { ellipsisVerticalOutline, play } from 'ionicons/icons'
import { createGoalStakeholder, Post, StoryItem, User } from '@strive/model'
@@ -14,7 +14,8 @@ import { getEnterAnimation, getLeaveAnimation, ImageZoomModalComponent } from '@
import { ImageDirective } from '@strive/media/directives/image.directive'
import { HTMLPipe } from '@strive/utils/pipes/string-to-html.pipe'
import { SafePipe } from '@strive/utils/pipes/safe-url.pipe'
-import { MediaRefPipe } from '@strive/media/pipes/media.pipe'
+import { MediaRefPipe, VideoUrlPipe } from '@strive/media/pipes/media.pipe'
+import { VideoPlayerComponent } from '@strive/media/components/video-player/video-player.component'
@Component({
standalone: true,
@@ -29,9 +30,11 @@ import { MediaRefPipe } from '@strive/media/pipes/media.pipe'
ImageDirective,
ImageZoomModalComponent,
PostOptionsComponent,
+ VideoPlayerComponent,
HTMLPipe,
SafePipe,
MediaRefPipe,
+ VideoUrlPipe,
IonCard,
IonAvatar,
IonButton,
@@ -60,7 +63,7 @@ export class PostComponent implements AfterViewInit {
private modalCtrl: ModalController,
private popoverCtrl: PopoverController
) {
- addIcons({ ellipsisVerticalOutline })
+ addIcons({ ellipsisVerticalOutline, play })
}
ngAfterViewInit() {
diff --git a/libs/post/src/lib/modals/upsert/post-upsert.component.html b/libs/post/src/lib/modals/upsert/post-upsert.component.html
index fe537488..2ba27bdf 100644
--- a/libs/post/src/lib/modals/upsert/post-upsert.component.html
+++ b/libs/post/src/lib/modals/upsert/post-upsert.component.html
@@ -75,7 +75,7 @@
-
+
@if (saving$ | async) {
}
diff --git a/libs/post/src/lib/modals/upsert/post-upsert.component.ts b/libs/post/src/lib/modals/upsert/post-upsert.component.ts
index f611d2b6..f6185a83 100644
--- a/libs/post/src/lib/modals/upsert/post-upsert.component.ts
+++ b/libs/post/src/lib/modals/upsert/post-upsert.component.ts
@@ -103,7 +103,7 @@ export class UpsertPostModalComponent extends ModalDirective implements AfterVie
if (!formValue.description) this.postForm.description.setValue(description ?? '')
if (image) {
- const editMediaForm = new EditMediaForm({ preview: image })
+ const editMediaForm = new EditMediaForm({ preview: image, type: 'image' })
this.postForm.controls.medias.push(editMediaForm)
}
}
@@ -137,6 +137,7 @@ export class UpsertPostModalComponent extends ModalDirective implements AfterVie
async submitPost() {
if (!this.auth.uid) return
+ if (this.postForm.invalid) return
this.saving$.next(true)
if (!this.postForm.isEmpty) {
diff --git a/libs/ui/src/lib/image-zoom/image-zoom.component.html b/libs/ui/src/lib/image-zoom/image-zoom.component.html
index e14dacca..3b8189f5 100644
--- a/libs/ui/src/lib/image-zoom/image-zoom.component.html
+++ b/libs/ui/src/lib/image-zoom/image-zoom.component.html
@@ -8,7 +8,12 @@
@for (media of medias; track media) {
-
+ @if (media.fileType === 'video') {
+
+ } @else {
+
+ }
+
}
diff --git a/libs/ui/src/lib/image-zoom/image-zoom.component.scss b/libs/ui/src/lib/image-zoom/image-zoom.component.scss
index 1530aecf..5d78d7a9 100644
--- a/libs/ui/src/lib/image-zoom/image-zoom.component.scss
+++ b/libs/ui/src/lib/image-zoom/image-zoom.component.scss
@@ -9,10 +9,10 @@ swiper-container {
align-items: center;
height: 100%;
- img {
+ img, video {
min-width: 100%;
aspect-ratio: 1/1;
- object-fit: cover;
+ object-fit: contain;
}
}
}
\ No newline at end of file
diff --git a/libs/ui/src/lib/image-zoom/image-zoom.component.ts b/libs/ui/src/lib/image-zoom/image-zoom.component.ts
index 2e4b395f..1ec5959e 100644
--- a/libs/ui/src/lib/image-zoom/image-zoom.component.ts
+++ b/libs/ui/src/lib/image-zoom/image-zoom.component.ts
@@ -4,8 +4,9 @@ import { createAnimation } from '@ionic/core'
import { Media } from '@strive/model'
import { ModalDirective } from '@strive/utils/directives/modal.directive'
-import { MediaRefPipe } from '@strive/media/pipes/media.pipe'
+import { MediaRefPipe, VideoUrlPipe } from '@strive/media/pipes/media.pipe'
import { ImageDirective } from '@strive/media/directives/image.directive'
+import { VideoPlayerComponent } from '@strive/media/components/video-player/video-player.component'
import { HeaderComponent } from '../header/header.component'
import { SwiperContainer } from 'swiper/element'
@@ -47,7 +48,9 @@ export function getLeaveAnimation(baseEl: HTMLElement) {
imports: [
ImageDirective,
MediaRefPipe,
- HeaderComponent
+ VideoUrlPipe,
+ HeaderComponent,
+ VideoPlayerComponent
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
diff --git a/package-lock.json b/package-lock.json
index 7c502e45..0a160ede 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "strive",
- "version": "1.15.3",
+ "version": "1.15.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "strive",
- "version": "1.15.3",
+ "version": "1.15.4",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -36,6 +36,7 @@
"@capacitor/share": "^6.0.0",
"@capacitor/splash-screen": "^6.0.0",
"@capawesome/capacitor-app-update": "^6.0.0",
+ "@capawesome/capacitor-file-picker": "^6.0.1",
"@ionic/angular": "^8.0.1",
"@ionic/pwa-elements": "^3.2.2",
"@nx/angular": "18.3.4",
@@ -3112,6 +3113,24 @@
"@capacitor/core": "^6.0.0"
}
},
+ "node_modules/@capawesome/capacitor-file-picker": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@capawesome/capacitor-file-picker/-/capacitor-file-picker-6.0.1.tgz",
+ "integrity": "sha512-JWYmqfYMRwvhJMACC4oovzBGtcP1MXnRKbENPjXhL0ni0p0nnVgddKD+07kc81SNiABBuCksQjNiFiEDqqy49A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/capawesome-team/"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/capawesome"
+ }
+ ],
+ "peerDependencies": {
+ "@capacitor/core": "^6.0.0"
+ }
+ },
"node_modules/@colors/colors": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
diff --git a/package.json b/package.json
index 1426cf55..bf2c6156 100644
--- a/package.json
+++ b/package.json
@@ -46,6 +46,7 @@
"@capacitor/share": "^6.0.0",
"@capacitor/splash-screen": "^6.0.0",
"@capawesome/capacitor-app-update": "^6.0.0",
+ "@capawesome/capacitor-file-picker": "^6.0.1",
"@ionic/angular": "^8.0.1",
"@ionic/pwa-elements": "^3.2.2",
"@nx/angular": "18.3.4",