-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8d550bc
commit 697b360
Showing
32 changed files
with
553 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { gcsBucket, onDocumentDelete } from '@strive/api/firebase' | ||
import { createMedia } from '@strive/model' | ||
import { toDate } from '../../../shared/utils' | ||
|
||
export const mediaDeletedHandler = onDocumentDelete(`Goals/{goalId}/Media/{mediaId}`, 'mediaDeletedHandler', | ||
async (snapshot) => { | ||
const media = createMedia(toDate({ ...snapshot.data(), id: snapshot.id })) | ||
|
||
const fileRef = `${media.storagePath}/${media.id}` | ||
gcsBucket.file(fileRef).delete({ ignoreNotFound: true }) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
libs/media/src/lib/components/images-selector/images-selector.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<swiper-container space-between="16" slides-per-view="auto" #swiper> | ||
<ng-container *ngFor="let control of form.controls"> | ||
<swiper-slide> | ||
<img [src]="control.value.preview" /> | ||
</swiper-slide> | ||
</ng-container> | ||
<swiper-slide> | ||
<section [ngSwitch]="step | async"> | ||
<!-- Drop --> | ||
<article *ngSwitchCase="'drop'" class="drop-zone" (click)="selectImages()"> | ||
<ion-icon name="images-outline" /> | ||
<span>Add Photo</span> | ||
</article> | ||
|
||
<!-- Hovering --> | ||
<article *ngSwitchCase="'hovering'" class="drop-zone hover" (click)="selectImages()"> | ||
<ion-icon name="images-outline" /> | ||
<span>Drop your file</span> | ||
</article> | ||
</section> | ||
</swiper-slide> | ||
</swiper-container> | ||
|
||
<input #fileUploader multiple hidden [accept]="accept" type="file" (change)="inputChange($event)" /> |
64 changes: 64 additions & 0 deletions
64
libs/media/src/lib/components/images-selector/images-selector.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// @import 'swiper/scss'; | ||
// @import 'swiper/scss/free-mode'; | ||
|
||
:host { | ||
display: flex; | ||
gap: 12px; | ||
overflow: auto; | ||
height: 160px; | ||
|
||
swiper-container { | ||
width: 100%; | ||
|
||
swiper-slide { | ||
max-width: 100%; | ||
width: fit-content; | ||
} | ||
} | ||
|
||
img { | ||
height: 100%; | ||
border-radius: 12px; | ||
} | ||
|
||
section { | ||
height: 100%; | ||
} | ||
|
||
article.drop-zone { | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: center; | ||
align-items: center; | ||
gap: 16px; | ||
height: 100%; | ||
border: 2px dashed var(--ion-color-primary); | ||
border-radius: 12px; | ||
width: 180px; | ||
box-sizing: border-box; | ||
|
||
&:hover { | ||
cursor: pointer; | ||
} | ||
|
||
p { | ||
white-space: nowrap; | ||
} | ||
|
||
div { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
margin-bottom: 1em; | ||
|
||
img { | ||
width: 100px; | ||
} | ||
} | ||
} | ||
|
||
article.hover { | ||
border: 2px solid var(--ion-color-primary); | ||
} | ||
} | ||
|
138 changes: 138 additions & 0 deletions
138
libs/media/src/lib/components/images-selector/images-selector.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostListener, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core' | ||
import { CommonModule } from '@angular/common' | ||
import { FormArray } from '@angular/forms' | ||
import { SafeUrl } from '@angular/platform-browser' | ||
import { IonicModule, ToastController } from '@ionic/angular' | ||
import { SwiperContainer } from 'swiper/swiper-element' | ||
|
||
import { BehaviorSubject } from 'rxjs' | ||
|
||
import { Camera, GalleryPhoto } from '@capacitor/camera' | ||
import { captureException, captureMessage } from '@sentry/capacitor' | ||
import { EditMediaForm } from '@strive/media/forms/media.form' | ||
import { delay } from '@strive/utils/helpers' | ||
|
||
type CropStep = 'drop' | 'hovering' | ||
|
||
@Component({ | ||
standalone: true, | ||
imports: [ | ||
CommonModule, | ||
IonicModule | ||
], | ||
schemas: [CUSTOM_ELEMENTS_SCHEMA], | ||
selector: '[form] strive-images-selector', | ||
templateUrl: 'images-selector.component.html', | ||
styleUrls: ['./images-selector.component.scss'], | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
encapsulation: ViewEncapsulation.ShadowDom | ||
}) | ||
export class ImagesSelectorComponent { | ||
|
||
step = new BehaviorSubject<CropStep>('drop') | ||
|
||
accept = ['.jpg', '.jpeg', '.png', '.webp'] | ||
previewUrl$ = new BehaviorSubject<string | SafeUrl>('') | ||
|
||
@Input() form!: FormArray<EditMediaForm> | ||
|
||
@ViewChild('fileUploader') fileUploader?: ElementRef<HTMLInputElement> | ||
@ViewChild('swiper') swiper?: ElementRef<SwiperContainer>; | ||
|
||
constructor( | ||
private cdr: ChangeDetectorRef, | ||
private toast: ToastController | ||
) {} | ||
|
||
@HostListener('drop', ['$event']) | ||
onDrop($event: DragEvent) { | ||
$event?.preventDefault() | ||
if ($event?.dataTransfer?.files.length) { | ||
this.filesSelected($event.dataTransfer.files) | ||
} | ||
this.step.next('drop') | ||
} | ||
|
||
@HostListener('dragover', ['$event']) | ||
onDragOver($event: DragEvent) { | ||
$event.preventDefault() | ||
this.step.next('hovering') | ||
} | ||
|
||
@HostListener('dragleave', ['$event']) | ||
onDragLeave($event: DragEvent) { | ||
$event.preventDefault() | ||
this.step.next('drop') | ||
} | ||
|
||
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) | ||
} else { | ||
this.toast.create({ message: 'Something went wrong', duration: 3000 }) | ||
captureMessage('Unsupported file type chosen') | ||
captureException(image) | ||
} | ||
} | ||
} catch (err) { | ||
this.fileUploader?.nativeElement.click() | ||
} | ||
} | ||
|
||
inputChange(event: any) { | ||
const files = event.target.files | ||
this.filesSelected(files) | ||
} | ||
|
||
filesSelected(file: FileList | File) { | ||
const files = isFileList(file) ? Array.from(file) : [file] | ||
|
||
for (const file of files) { | ||
if (file.type?.split('/')[0] !== 'image') { | ||
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 }) | ||
this.form.push(mediaForm) | ||
this.form.markAsDirty() | ||
} | ||
|
||
this.cdr.markForCheck() | ||
this.step.next('drop') | ||
|
||
if (this.fileUploader) { | ||
this.fileUploader.nativeElement.value = '' | ||
} | ||
if (this.swiper) { | ||
const swiper = this.swiper.nativeElement.swiper | ||
delay(200).then(() => { | ||
swiper.slideTo(this.form.length + 1) | ||
}) | ||
} | ||
} | ||
} | ||
|
||
function isFileList(file: FileList | File): file is FileList { | ||
return (file as FileList).item !== undefined | ||
} | ||
|
||
|
||
async function getFileFromGalleryPhoto(photo: GalleryPhoto): Promise<File | undefined> { | ||
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; | ||
} | ||
} |
Oops, something went wrong.