fix error sharing image and failing to upload, fix upload in webkit/safari, and test it
This commit is contained in:
@@ -350,6 +350,7 @@ export default class PhotoDialog extends Vue {
|
||||
const token = await accessToken(this.activeDid);
|
||||
const headers = {
|
||||
Authorization: "Bearer " + token,
|
||||
// axios fills in Content-Type of multipart/form-data
|
||||
};
|
||||
const formData = new FormData();
|
||||
if (!this.blob) {
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
export type Temp = {
|
||||
id: string;
|
||||
blob?: Blob;
|
||||
blob?: Blob; // deprecated because webkit (Safari) does not support Blob
|
||||
blobB64?: string; // base64-encoded blob
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,7 @@ import { createPeerDid } from "@/libs/crypto/vc/didPeer";
|
||||
|
||||
export const PRIVACY_MESSAGE =
|
||||
"The data you send will be visible to the world -- except: your IDs and the IDs of anyone you tag will stay private, only visible to them and others you explicitly allow.";
|
||||
export const SHARED_PHOTO_BASE64_KEY = "shared-photo-base64";
|
||||
|
||||
/* eslint-disable prettier/prettier */
|
||||
export const UNIT_SHORT: Record<string, string> = {
|
||||
@@ -116,6 +117,38 @@ export const isGiveRecordTheUserCanConfirm = (
|
||||
);
|
||||
};
|
||||
|
||||
export async function blobToBase64(blob: Blob): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => resolve(reader.result as string); // potential problem if it returns an ArrayBuffer?
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
}
|
||||
|
||||
export function base64ToBlob(base64DataUrl: string, sliceSize = 512) {
|
||||
// Extract the content type and the Base64 data
|
||||
const [metadata, base64] = base64DataUrl.split(",");
|
||||
const contentTypeMatch = metadata.match(/data:(.*?);base64/);
|
||||
const contentType = contentTypeMatch ? contentTypeMatch[1] : "";
|
||||
|
||||
const byteCharacters = atob(base64);
|
||||
const byteArrays = [];
|
||||
|
||||
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
|
||||
const slice = byteCharacters.slice(offset, offset + sliceSize);
|
||||
|
||||
const byteNumbers = new Array(slice.length);
|
||||
for (let i = 0; i < slice.length; i++) {
|
||||
byteNumbers[i] = slice.charCodeAt(i);
|
||||
}
|
||||
|
||||
const byteArray = new Uint8Array(byteNumbers);
|
||||
byteArrays.push(byteArray);
|
||||
}
|
||||
return new Blob(byteArrays, { type: contentType });
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns the DID of the person who offered, or undefined if hidden
|
||||
* @param veriClaim is expected to have fields: claim and issuer
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center mt-4">
|
||||
<div class="flex justify-center mt-4" data-testid="imagery">
|
||||
<span v-if="imageUrl" class="flex justify-between">
|
||||
<a :href="imageUrl" target="_blank" class="text-blue-500 ml-4">
|
||||
<img :src="imageUrl" class="h-24 rounded-xl" />
|
||||
|
||||
@@ -277,7 +277,10 @@
|
||||
</router-link>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="record.image" class="flex justify-center">
|
||||
<div
|
||||
v-if="record.image"
|
||||
class="flex justify-center"
|
||||
>
|
||||
<a :href="record.image" target="_blank">
|
||||
<img :src="record.image" class="h-24 mt-2 rounded-xl" />
|
||||
</a>
|
||||
|
||||
@@ -66,7 +66,8 @@ import {
|
||||
} from "@/constants/app";
|
||||
import { db } from "@/db/index";
|
||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||
import { getHeaders } from "@/libs/endorserServer";
|
||||
import { accessToken } from "@/libs/crypto";
|
||||
import { base64ToBlob, SHARED_PHOTO_BASE64_KEY } from "@/libs/util";
|
||||
|
||||
@Component({ components: { PhotoDialog, QuickNav } })
|
||||
export default class SharedPhotoView extends Vue {
|
||||
@@ -86,16 +87,19 @@ export default class SharedPhotoView extends Vue {
|
||||
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
||||
this.activeDid = settings?.activeDid as string;
|
||||
|
||||
const temp = await db.temp.get("shared-photo");
|
||||
const temp = await db.temp.get(SHARED_PHOTO_BASE64_KEY);
|
||||
const imageB64 = temp?.blobB64 as string;
|
||||
if (temp) {
|
||||
this.imageBlob = temp.blob;
|
||||
this.imageBlob = base64ToBlob(imageB64);
|
||||
|
||||
// clear the temp image
|
||||
db.temp.delete("shared-photo");
|
||||
db.temp.delete(SHARED_PHOTO_BASE64_KEY);
|
||||
|
||||
this.imageFileName = (this.$route as Router).query[
|
||||
"fileName"
|
||||
] as string;
|
||||
} else {
|
||||
console.error("No appropriate image found in temp storage.", temp);
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
console.error("Got an error loading an identifier:", err);
|
||||
@@ -156,7 +160,11 @@ export default class SharedPhotoView extends Vue {
|
||||
let result;
|
||||
try {
|
||||
// send the image to the server
|
||||
const headers = await getHeaders(this.activeDid);
|
||||
const token = await accessToken(this.activeDid);
|
||||
const headers = {
|
||||
Authorization: "Bearer " + token,
|
||||
// axios fills in Content-Type of multipart/form-data
|
||||
};
|
||||
const formData = new FormData();
|
||||
formData.append(
|
||||
"image",
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
<div class="mt-8">
|
||||
<h2 class="text-xl font-bold mb-4">Image Sharing</h2>
|
||||
Populates the "shared-photo" view as if they used "share_target".
|
||||
<input type="file" @change="uploadFile" />
|
||||
<input type="file" data-testid="fileInput" @change="uploadFile" />
|
||||
<router-link
|
||||
v-if="showFileNextStep()"
|
||||
:to="{
|
||||
@@ -165,6 +165,7 @@
|
||||
query: { fileName },
|
||||
}"
|
||||
class="block w-full text-center text-md 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-1.5 py-2 rounded-md mb-2 mt-2"
|
||||
data-testid="fileUploadButton"
|
||||
>
|
||||
Go to Shared Page
|
||||
</router-link>
|
||||
@@ -257,8 +258,10 @@ import {
|
||||
} from "@/libs/crypto/vc/passkeyDidPeer";
|
||||
import {
|
||||
AccountKeyInfo,
|
||||
blobToBase64,
|
||||
getAccount,
|
||||
registerAndSavePasskey,
|
||||
SHARED_PHOTO_BASE64_KEY,
|
||||
} from "@/libs/util";
|
||||
|
||||
const inputFileNameRef = ref<Blob>();
|
||||
@@ -320,12 +323,13 @@ export default class Help extends Vue {
|
||||
const blob = new Blob([new Uint8Array(data)], {
|
||||
type: file.type,
|
||||
});
|
||||
const blobB64 = await blobToBase64(blob);
|
||||
this.fileName = file.name as string;
|
||||
const temp = await db.temp.get("shared-photo");
|
||||
const temp = await db.temp.get(SHARED_PHOTO_BASE64_KEY);
|
||||
if (temp) {
|
||||
await db.temp.update("shared-photo", { blob });
|
||||
await db.temp.update(SHARED_PHOTO_BASE64_KEY, { blobB64 });
|
||||
} else {
|
||||
await db.temp.add({ id: "shared-photo", blob });
|
||||
await db.temp.add({ id: SHARED_PHOTO_BASE64_KEY, blobB64 });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user