Browse Source
			
			
			
			
				
		Reviewed-on: https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/pulls/115
				 18 changed files with 553 additions and 132 deletions
			
			
		| @ -0,0 +1,13 @@ | |||
| // for ephemeral uses, eg. passing a blob from the service worker to the main thread
 | |||
| 
 | |||
| export type Temp = { | |||
|   id: string; | |||
|   blob?: Blob; | |||
| }; | |||
| 
 | |||
| /** | |||
|  * Schema for the Temp table in the database. | |||
|  */ | |||
| export const TempSchema = { | |||
|   temp: "id", | |||
| }; | |||
| @ -0,0 +1,188 @@ | |||
| <template> | |||
|   <QuickNav /> | |||
|   <!-- CONTENT --> | |||
|   <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> | |||
|     <!-- Heading --> | |||
|     <h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8"> | |||
|       Image | |||
|     </h1> | |||
|     <div v-if="imageBlob"> | |||
|       <div v-if="uploading" class="text-center mb-4"> | |||
|         <fa icon="spinner" class="fa-spin-pulse" /> | |||
|       </div> | |||
|       <div v-else> | |||
|         <div class="text-center mb-4">Choose how to use this image</div> | |||
|         <div class="grid grid-cols-1 sm:grid-cols-3 gap-4"> | |||
|           <button | |||
|             @click="recordGift" | |||
|             class="text-center text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md" | |||
|           > | |||
|             <fa icon="gift" class="fa-fw" /> | |||
|             Record a Gift | |||
|           </button> | |||
|           <button | |||
|             @click="recordProfile" | |||
|             class="text-center text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md" | |||
|           > | |||
|             <fa icon="circle-user" class="fa-fw" /> | |||
|             Save as Profile Image | |||
|           </button> | |||
|           <button | |||
|             @click="cancel" | |||
|             class="text-center text-md font-bold bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md" | |||
|           > | |||
|             <fa icon="ban" class="fa-fw" /> | |||
|             Cancel | |||
|           </button> | |||
|         </div> | |||
|       </div> | |||
| 
 | |||
|       <div class="flex justify-center"> | |||
|         <img | |||
|           :src="URL.createObjectURL(imageBlob)" | |||
|           alt="Shared Image" | |||
|           class="rounded mt-4" | |||
|         /> | |||
|       </div> | |||
|     </div> | |||
|     <div v-else class="text-center mb-4"> | |||
|       <p>No image found.</p> | |||
|     </div> | |||
|   </section> | |||
| </template> | |||
| 
 | |||
| <script lang="ts"> | |||
| import { Component, Vue } from "vue-facing-decorator"; | |||
| 
 | |||
| import QuickNav from "@/components/QuickNav.vue"; | |||
| import { | |||
|   DEFAULT_IMAGE_API_SERVER, | |||
|   IMAGE_TYPE_PROFILE, | |||
|   NotificationIface, | |||
| } from "@/constants/app"; | |||
| import { db } from "@/db/index"; | |||
| import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; | |||
| import { getIdentity } from "@/libs/util"; | |||
| import { accessToken } from "@/libs/crypto"; | |||
| import axios from "axios"; | |||
| 
 | |||
| @Component({ components: { QuickNav } }) | |||
| export default class SharedPhotoView extends Vue { | |||
|   $notify!: (notification: NotificationIface, timeout?: number) => void; | |||
| 
 | |||
|   activeDid: string | undefined = undefined; | |||
|   imageBlob: Blob | undefined = undefined; | |||
|   imageFileName: string | undefined = undefined; | |||
|   uploading = false; | |||
| 
 | |||
|   URL = window.URL || window.webkitURL; | |||
| 
 | |||
|   // 'created' hook runs when the Vue instance is first created | |||
|   async mounted() { | |||
|     try { | |||
|       await db.open(); | |||
|       const settings = await db.settings.get(MASTER_SETTINGS_KEY); | |||
|       this.activeDid = settings?.activeDid as string; | |||
| 
 | |||
|       const temp = await db.temp.get("shared-photo"); | |||
|       if (temp) { | |||
|         this.imageBlob = temp.blob; | |||
| 
 | |||
|         // clear the temp image | |||
|         db.temp.delete("shared-photo"); | |||
| 
 | |||
|         this.imageFileName = this.$route.query.fileName as string; | |||
|       } | |||
|     } catch (err: unknown) { | |||
|       console.error("Got an error loading an identifier:", err); | |||
|       this.$notify( | |||
|         { | |||
|           group: "alert", | |||
|           type: "danger", | |||
|           title: "Error", | |||
|           text: "Got an error loading this data.", | |||
|         }, | |||
|         -1, | |||
|       ); | |||
|     } | |||
|   } | |||
| 
 | |||
|   async recordGift() { | |||
|     await this.sendToImageServer("GiveAction").then((url) => { | |||
|       if (url) { | |||
|         this.$router.push({ | |||
|           name: "gifted-details", | |||
|           query: { | |||
|             destinationNameAfter: "home", | |||
|             hideBackButton: true, | |||
|             imageUrl: url, | |||
|             recipientDid: this.activeDid, | |||
|           }, | |||
|         }); | |||
|       } | |||
|     }); | |||
|   } | |||
| 
 | |||
|   async recordProfile() { | |||
|     await this.sendToImageServer(IMAGE_TYPE_PROFILE).then((url) => { | |||
|       if (url) { | |||
|         db.settings.update(MASTER_SETTINGS_KEY, { | |||
|           profileImageUrl: url, | |||
|         }); | |||
|         this.$router.push({ name: "account" }); | |||
|       } | |||
|     }); | |||
|   } | |||
| 
 | |||
|   async cancel() { | |||
|     this.imageBlob = undefined; | |||
|     this.imageFileName = undefined; | |||
|     this.$router.push({ name: "home" }); | |||
|   } | |||
| 
 | |||
|   async sendToImageServer(imageType: string) { | |||
|     this.uploading = true; | |||
| 
 | |||
|     let result; | |||
|     try { | |||
|       // send the image to the server | |||
|       const identifier = await getIdentity(this.activeDid as string); | |||
|       const token = await accessToken(identifier); | |||
|       const headers = { | |||
|         Authorization: "Bearer " + token, | |||
|       }; | |||
|       const formData = new FormData(); | |||
|       formData.append( | |||
|         "image", | |||
|         this.imageBlob as Blob, | |||
|         this.imageFileName as string, | |||
|       ); | |||
|       formData.append("claimType", imageType); | |||
| 
 | |||
|       const response = await axios.post( | |||
|         DEFAULT_IMAGE_API_SERVER + "/image", | |||
|         formData, | |||
|         { headers }, | |||
|       ); | |||
|       this.imageBlob = undefined; | |||
|       this.imageFileName = undefined; | |||
| 
 | |||
|       this.uploading = false; | |||
|       result = response.data.url as string; | |||
|     } catch (error) { | |||
|       console.error("Error uploading the image", error); | |||
|       this.$notify( | |||
|         { | |||
|           group: "alert", | |||
|           type: "danger", | |||
|           title: "Error", | |||
|           text: "There was an error saving the picture. Please try again.", | |||
|         }, | |||
|         5000, | |||
|       ); | |||
|       this.uploading = false; | |||
|     } | |||
|     return result; | |||
|   } | |||
| } | |||
| </script> | |||
					Loading…
					
					
				
		Reference in new issue