|  |  | @ -13,7 +13,7 @@ | 
			
		
	
		
			
				
					|  |  |  |       </div> | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       <!-- Heading --> | 
			
		
	
		
			
				
					|  |  |  |       <h1 id="ViewHeading" class="text-4xl text-center font-light pt-4"> | 
			
		
	
		
			
				
					|  |  |  |       <h1 id="ViewHeading" class="text-center font-bold"> | 
			
		
	
		
			
				
					|  |  |  |         <span v-if="uploading"> Uploading... </span> | 
			
		
	
		
			
				
					|  |  |  |         <span v-else-if="blob"> Look Good? </span> | 
			
		
	
		
			
				
					|  |  |  |         <span v-else> Say "Cheese"! </span> | 
			
		
	
	
		
			
				
					|  |  | @ -30,6 +30,7 @@ | 
			
		
	
		
			
				
					|  |  |  |           > | 
			
		
	
		
			
				
					|  |  |  |             <span>Upload</span> | 
			
		
	
		
			
				
					|  |  |  |           </button> | 
			
		
	
		
			
				
					|  |  |  |           <!--{{ imageHeight }} {{ imageWidth }} {{ imageWarning }}--> | 
			
		
	
		
			
				
					|  |  |  |           <button | 
			
		
	
		
			
				
					|  |  |  |             @click="retryImage" | 
			
		
	
		
			
				
					|  |  |  |             class="bg-slate-500 hover:bg-slate-700 text-white font-bold py-2 px-2 rounded-full" | 
			
		
	
	
		
			
				
					|  |  | @ -37,16 +38,20 @@ | 
			
		
	
		
			
				
					|  |  |  |             <span>Retry</span> | 
			
		
	
		
			
				
					|  |  |  |           </button> | 
			
		
	
		
			
				
					|  |  |  |         </div> | 
			
		
	
		
			
				
					|  |  |  |         <img :src="URL.createObjectURL(blob)" class="mt-2 w-full" /> | 
			
		
	
		
			
				
					|  |  |  |         <div | 
			
		
	
		
			
				
					|  |  |  |           :style="'height: ' + imageHeight + 'px; width: ' + imageWidth + 'px;'" | 
			
		
	
		
			
				
					|  |  |  |         > | 
			
		
	
		
			
				
					|  |  |  |           <img :src="URL.createObjectURL(blob)" class="mt-2" /> | 
			
		
	
		
			
				
					|  |  |  |         </div> | 
			
		
	
		
			
				
					|  |  |  |       </div> | 
			
		
	
		
			
				
					|  |  |  |       <div v-else> | 
			
		
	
		
			
				
					|  |  |  |         <!-- | 
			
		
	
		
			
				
					|  |  |  |           Camera "resolution" doesn't change how it shows on screen but rather stretches the result, eg the following which just stretches it vertically: | 
			
		
	
		
			
				
					|  |  |  |           :resolution="{ width: 375, height: 812 }" | 
			
		
	
		
			
				
					|  |  |  |           It even messes up when trying to match the ratio with imageHeight and imageWidth. | 
			
		
	
		
			
				
					|  |  |  |         --> | 
			
		
	
		
			
				
					|  |  |  |         <camera facingMode="environment" autoplay ref="camera"> | 
			
		
	
		
			
				
					|  |  |  |           <div class="absolute bottom-0 w-full flex justify-center pb-4"> | 
			
		
	
		
			
				
					|  |  |  |             <!-- Button --> | 
			
		
	
		
			
				
					|  |  |  |           <div class="absolute top-0 w-full flex justify-center pb-4"> | 
			
		
	
		
			
				
					|  |  |  |             <button | 
			
		
	
		
			
				
					|  |  |  |               @click="takeImage" | 
			
		
	
		
			
				
					|  |  |  |               class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-2 rounded-full" | 
			
		
	
	
		
			
				
					|  |  | @ -78,6 +83,9 @@ export default class GiftedPhotoDialog extends Vue { | 
			
		
	
		
			
				
					|  |  |  |   activeDid = ""; | 
			
		
	
		
			
				
					|  |  |  |   blob: Blob | null = null; | 
			
		
	
		
			
				
					|  |  |  |   setImage: (arg: string) => void = () => {}; | 
			
		
	
		
			
				
					|  |  |  |   imageHeight?: number = window.innerHeight / 2; | 
			
		
	
		
			
				
					|  |  |  |   imageWidth?: number = window.innerWidth / 2; | 
			
		
	
		
			
				
					|  |  |  |   imageWarning = "."; | 
			
		
	
		
			
				
					|  |  |  |   uploading = false; | 
			
		
	
		
			
				
					|  |  |  |   visible = false; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  | @ -115,7 +123,55 @@ export default class GiftedPhotoDialog extends Vue { | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   async takeImage(/* payload: MouseEvent */) { | 
			
		
	
		
			
				
					|  |  |  |     const cameraComponent = this.$refs.camera as InstanceType<typeof Camera>; | 
			
		
	
		
			
				
					|  |  |  |     this.blob = await cameraComponent?.snapshot(); // png is default; if that changes, change extension in formData.append | 
			
		
	
		
			
				
					|  |  |  |     console.log("cameraComponent", cameraComponent, cameraComponent.resolution); | 
			
		
	
		
			
				
					|  |  |  |     this.imageHeight = cameraComponent?.resolution?.height; | 
			
		
	
		
			
				
					|  |  |  |     this.imageWidth = cameraComponent?.resolution?.width; | 
			
		
	
		
			
				
					|  |  |  |     const initialImageRatio = this.imageWidth / this.imageHeight; | 
			
		
	
		
			
				
					|  |  |  |     console.log( | 
			
		
	
		
			
				
					|  |  |  |       "initialImageRatio", | 
			
		
	
		
			
				
					|  |  |  |       initialImageRatio, | 
			
		
	
		
			
				
					|  |  |  |       this.imageWidth, | 
			
		
	
		
			
				
					|  |  |  |       this.imageHeight, | 
			
		
	
		
			
				
					|  |  |  |     ); | 
			
		
	
		
			
				
					|  |  |  |     const windowRatio = window.innerWidth / window.innerHeight; | 
			
		
	
		
			
				
					|  |  |  |     console.log( | 
			
		
	
		
			
				
					|  |  |  |       "windowRatio", | 
			
		
	
		
			
				
					|  |  |  |       windowRatio, | 
			
		
	
		
			
				
					|  |  |  |       window.innerWidth, | 
			
		
	
		
			
				
					|  |  |  |       window.innerHeight, | 
			
		
	
		
			
				
					|  |  |  |     ); | 
			
		
	
		
			
				
					|  |  |  |     if (initialImageRatio > 1 && windowRatio < 1) { | 
			
		
	
		
			
				
					|  |  |  |       this.imageWarning = "?"; | 
			
		
	
		
			
				
					|  |  |  |       // For some reason, mobile in portrait orientation renders a horizontally-stretched image. | 
			
		
	
		
			
				
					|  |  |  |       // We're gonna force it opposite. | 
			
		
	
		
			
				
					|  |  |  |       this.imageHeight = cameraComponent?.resolution?.width; | 
			
		
	
		
			
				
					|  |  |  |       this.imageWidth = cameraComponent?.resolution?.height; | 
			
		
	
		
			
				
					|  |  |  |     } else if (initialImageRatio < 1 && windowRatio > 1) { | 
			
		
	
		
			
				
					|  |  |  |       // Haven't seen this happen, but just in case. | 
			
		
	
		
			
				
					|  |  |  |       this.imageWarning = "??"; | 
			
		
	
		
			
				
					|  |  |  |       this.imageHeight = cameraComponent?.resolution?.width; | 
			
		
	
		
			
				
					|  |  |  |       this.imageWidth = cameraComponent?.resolution?.height; | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |     const newImageRatio = this.imageWidth / this.imageHeight; | 
			
		
	
		
			
				
					|  |  |  |     console.log( | 
			
		
	
		
			
				
					|  |  |  |       "newImageRatio", | 
			
		
	
		
			
				
					|  |  |  |       newImageRatio, | 
			
		
	
		
			
				
					|  |  |  |       this.imageWidth, | 
			
		
	
		
			
				
					|  |  |  |       this.imageHeight, | 
			
		
	
		
			
				
					|  |  |  |     ); | 
			
		
	
		
			
				
					|  |  |  |     if (newImageRatio < windowRatio) { | 
			
		
	
		
			
				
					|  |  |  |       this.imageHeight = window.innerHeight / 2; | 
			
		
	
		
			
				
					|  |  |  |       this.imageWidth = this.imageHeight * newImageRatio; | 
			
		
	
		
			
				
					|  |  |  |     } else { | 
			
		
	
		
			
				
					|  |  |  |       this.imageWidth = window.innerWidth / 2; | 
			
		
	
		
			
				
					|  |  |  |       this.imageHeight = this.imageWidth / newImageRatio; | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |     // The resolution is only necessary because of that mobile portrait-orientation case. | 
			
		
	
		
			
				
					|  |  |  |     // But setting this screws up desktop the mobile emulation. | 
			
		
	
		
			
				
					|  |  |  |     this.blob = await cameraComponent?.snapshot({ | 
			
		
	
		
			
				
					|  |  |  |       height: this.imageHeight, | 
			
		
	
		
			
				
					|  |  |  |       width: this.imageWidth, | 
			
		
	
		
			
				
					|  |  |  |     }); // png is default; if that changes, change extension in formData.append | 
			
		
	
		
			
				
					|  |  |  |     if (!this.blob) { | 
			
		
	
		
			
				
					|  |  |  |       this.$notify( | 
			
		
	
		
			
				
					|  |  |  |         { | 
			
		
	
	
		
			
				
					|  |  | @ -134,6 +190,45 @@ export default class GiftedPhotoDialog extends Vue { | 
			
		
	
		
			
				
					|  |  |  |     this.blob = null; | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   /**** | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |    Here's an approach without a library, which works similarly and looks ugly. | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |    <button id="start-camera" @click="cameraClicked">Start Camera</button> | 
			
		
	
		
			
				
					|  |  |  |    <video id="video" width="320" height="240" autoplay></video> | 
			
		
	
		
			
				
					|  |  |  |    <button id="snap-photo" @click="photoSnapped">Snap Photo</button> | 
			
		
	
		
			
				
					|  |  |  |    <canvas id="canvas" width="320" height="240"></canvas> | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   async cameraClicked() { | 
			
		
	
		
			
				
					|  |  |  |     console.log("camera_button clicked"); | 
			
		
	
		
			
				
					|  |  |  |     const video = document.querySelector("#video"); | 
			
		
	
		
			
				
					|  |  |  |     const stream = await navigator.mediaDevices.getUserMedia({ | 
			
		
	
		
			
				
					|  |  |  |       video: true, | 
			
		
	
		
			
				
					|  |  |  |       audio: false, | 
			
		
	
		
			
				
					|  |  |  |     }); | 
			
		
	
		
			
				
					|  |  |  |     if (video instanceof HTMLVideoElement) { | 
			
		
	
		
			
				
					|  |  |  |       video.srcObject = stream; | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |   photoSnapped() { | 
			
		
	
		
			
				
					|  |  |  |     const video = document.querySelector("#video"); | 
			
		
	
		
			
				
					|  |  |  |     const canvas = document.querySelector("#canvas"); | 
			
		
	
		
			
				
					|  |  |  |     console.log("snap_photo clicked"); | 
			
		
	
		
			
				
					|  |  |  |     if ( | 
			
		
	
		
			
				
					|  |  |  |       canvas instanceof HTMLCanvasElement && | 
			
		
	
		
			
				
					|  |  |  |       video instanceof HTMLVideoElement | 
			
		
	
		
			
				
					|  |  |  |     ) { | 
			
		
	
		
			
				
					|  |  |  |       canvas | 
			
		
	
		
			
				
					|  |  |  |         ?.getContext("2d") | 
			
		
	
		
			
				
					|  |  |  |         ?.drawImage(video, 0, 0, canvas.width, canvas.height); | 
			
		
	
		
			
				
					|  |  |  |       const image_data_url = canvas?.toDataURL("image/jpeg"); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       // data url of the image | 
			
		
	
		
			
				
					|  |  |  |       console.log(image_data_url); | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |   ****/ | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   async uploadImage() { | 
			
		
	
		
			
				
					|  |  |  |     this.uploading = true; | 
			
		
	
		
			
				
					|  |  |  |     const identifier = await getIdentity(this.activeDid); | 
			
		
	
	
		
			
				
					|  |  | 
 |